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

时间:2010-10-12 17:59:31

标签: powershell coding-style

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

非工作示例

$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*

工作示例

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

# 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}

}

为什么?

我猜这是因为PS没有加载函数。

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

6 个答案:

答案 0 :(得分:54)

请记住,一般来说,脚本中的工作原理应该在命令行

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

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

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

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

F
function F { "Hello, World!" }

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

function Main 
{
    F
}

function F
{
    "Hello, World!"
}

Main

答案 1 :(得分:29)

重新排序脚本

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

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

前瞻性声明

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

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

答案 2 :(得分:9)

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

步骤-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}
}

答案 3 :(得分:3)

除了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}

}

工作得很好

答案 4 :(得分:1)

对不起,我不得不发表评论。 CMD \ Batch确实允许您像C#一样在main方法下面声明函数。

@ECHO OFF
::Main
CALL :Function1
CALL :Function2
EXIT /b

::Functions
:Function1
    (ECHO Hello) & (ECHO World) 
EXIT /b

:Function2
    (ECHO Foo) & (ECHO Bar) 
EXIT /b 

答案 5 :(得分: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