为什么我需要先在PowerShell脚本中编写函数?

问题描述 投票:38回答:5

我有一个脚本,我利用函数来包装部分代码,允许我在指定的点移动部分。我发现我必须在脚本中首先列出函数才能正确运行。

Non-working example

$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

# Steps.ps1 
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

错误

这给我以下错误:

术语“Step1”未被识别为cmdlet,函数,脚本文件或可操作程序的名称。检查名称的拼写,或者如果包含路径,请验证路径是否正确,然后重试。

 At C:\Tools\Scripts\functiontest.ps1:7 char:12
  +     1{Step1 <<<< }
  + CategoryInfo          : ObjectNotFound: (Step1:String) [], CommandNotFoundException
  + FullyQualifiedErrorId : CommandNotFoundException*

Working example

如果我改变它的顺序它工作正常:

# Steps.ps1 
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

#steps
$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

Why?

我猜这是因为PS没有加载这些功能。

为什么这样,有没有更好的方法来布局这个代码结构?

powershell coding-style
5个回答
33
投票

Reorder your script

PowerShell是一个脚本,而不是一个编译语言。因此,它逐行遍历脚本,从上到下,(在标记脚本之后)并沿途评估每个命令。如果还没有得到函数的定义并且您已经尝试调用该函数,那么PowerShell将抛出错误。

因此,在这种情况下,您必须在switch语句之前移动函数定义 - 正如您所发现的那样。

前瞻性声明

甚至一些编译语言也是这样的,最着名的是C / C ++,并且需要forward declarations来解决这个问题。

其他编译语言(如C#)在编译期间对代码进行多次传递,因此不需要前向声明。


55
投票

请记住,通常,脚本中的工作应该在命令行中起作用。

在CMD中并非如此。 GOTOFOR %I IN (...) DO %%I就是两个例子。

在PowerShell中,我可以在命令行运行命令,直到得到我想要的结果,然后将历史记录粘贴到脚本中,然后编辑掉多余的位。

此外,我可以使用无法正常工作的脚本,将其粘贴到交互式shell中,并研究生成的状态。

在交互式命令行中,你无法写出这个:

F
function F { "Hello, World!" }

但是,在阅读脚本时,我想首先阅读顶级代码,然后在向下滚动时查看更多详细信息。一种方法是:

function Main 
{
    F
}

function F
{
    "Hello, World!"
}

Main

11
投票

您还可以从单独的文件中获取函数定义:

步骤,Lib.ps1

# Since this is just function definitions it is safe to source
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

Steps.ps1

# This sources the Steps-Lib.ps1 so that the functions are available
. "./Steps-Lib.ps1"

$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}
}

4
投票

除了Keith关于翻译顺序的说法,它也是Powershell设计的一部分。它真正意味着作为CLR对象的接口,甚至它自己的cmdlet。因此,在powershell“脚本编写”中,您不再需要构建这个大规模复杂的操作列表,而是将更多其他更小的逻辑集合在一起,并定义如何与它们进行交互。

在没有进入准宗教的Powershell和OOP讨论中,实现所需内容的最简单方法是将所有函数都隐藏在一个单独的文件中(称之为functions.ps1),然后在开头包含它。

所以假设一切都在functions1.ps1中

做一个

$functions = "$($MyInvocation.MyCommand.path | split-path)\functions.ps1"
. $functions

然后

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

会工作得很好


0
投票

来自Microsoft博客的解决方案,将主代码包含在一个块中,最后调用,

$MainFunction={
   $stepChoice = read-host 'Where would you like to start.'
   switch($stepChoice)
   {
       1{Step1}
       2{Step2}
       3{Step3}
   }
}
# Steps.ps1 
function Step1 { 
  'Step 1' 
   Step2 
} 
function Step2 { 
  'Step 2' 
   Step3 
} 
function Step3 { 
  'Step 3' 
  'Done!' 
}
#This line executes the program
& $MainFunction
© www.soinside.com 2019 - 2024. All rights reserved.