PowerShell 中函数是一系列 PowerShell 语句的组合。当你通过函数的名称调用函数时,函数中的语句会被顺序的执行,就像在命令行中执行它们一样。
从 hello world 开始
function Say-HelloWorld
{ Write-Host "hello world!" }
定义函数必须使用 function 关键字,并且为函数提供一个名称。然后用花括号把 PowerShell 语句括起来。
调用函数的方式也很简单,比如上面的函数连参数都没有定义,直接写函数名称就可以了:
> Say-HelloWorld
函数的命名规则
PowerShell 中命令的命名是非常有特色的,都是 "动词 - 名词" 的模式。MS 也建议我们以同样的模式来命名自定义的函数,以保证代码的可读性。
比如我们定义的函数 Say-HelloWorld 就遵循这样的规则。再举个例子,比如你要定义一个把日志信息写到文件中的函数,那可能就要命名为 Write-Log 之类的名称了。
MS 甚至对常用的名称给出了具体的规范,有兴趣的同学可以去读读:Approved Verbs for Windows PowerShell Commands。
位置参数 $args
在 shell 世界中位置参数(Position Patameters)是普遍存在的。其实就是在函数或脚本中我们可以通过默认存在的变量 $args 来引用传递给函数或脚本的参数。$args 变量是一个数组,所以我们需要通过索引语法来引用具体的参数,如 $args[0],$args[1] …
我们在下面的函数中引用位置参数:
function Say-Hello { Write-Host("Hello {0} {1}!" -f $args[0], $args[1]) }
> Say-Hello "Nick" "Li"
命名参数
除了使用位置参数,我们还可以在声明函数时指定命名参数。使用命名的参数还可以为参数指定默认值。
function Say-Hello($name, $count=3) { for(;$count -gt 0;$count--) { Write-Host("Hello {0}!" -f $name) } }
> Say-Hello "Nick" 2
作为 C# 程序员笔者比较喜欢把参数写在小括号里面,但是在 PowerShell 中更常见的参数声明方式为使用 Param 关键字:
function Say-Hello { Param ( $name, $count=3 ) for(;$count -gt 0;$count--) { Write-Host("Hello {0}!" -f $name) } }
这两种声明函数参数的方式是完全等价的,所以你可以选择自己喜欢的方式。
在调用函数时,我们还可以通过指定参数名称的方式为函数传参:
> Say-Hello -name "Jack" -count 5
从 Pipeline 输入函数参数
PowerShell 中的函数可以很轻松的支持从 Pipeline 输入参数,下面是一个简单的 demo:
function Say-Hello { Param ( [parameter(Mandatory=$true, ValueFromPipeline=$true)] $name ) process { Write-Host("Hello {0}!" -f $name) } }
在上面的函数声明中我们把函数的主体放入了 process 程序块。在 process 块中的语句会根据 Pipeline 中对象的个数执行多次。也就是说,如果我们通过 Pipeline 为函数传递 3 个参数,那么 process 块中的代码就会执行 3 次。
[parameter(Mandatory=$true, ValueFromPipeline=$true)] 说明可以通过 Pipeline 传递参数 name。试一下通过 Pipeline 传参:
> "Jack","Nick","Alice" | Say-Hello
注意,此时还可以通过 Say-Hello "Nick" 的方式调用函数,我们只是在基本功能的基础上添加了对 Pipeline 的支持。
作用域
函数只存在于创建它的上下文中(scope),比如定义函数的脚本。如果想要在一个脚本中引用在另外一个脚本中定义的函数,需要使用 source(.) 命令。在当前的脚本 A 中使用 source 命令并指定另外一个脚本 B 作为参数,会把 B 中定义的函数、别名和变量引入到脚本 A 的上下文中(scope)。
比如笔者在 Write-Log.ps1 文件中定义了函数 Write-Log,在另外一个脚本中使用 Write-Log 的方法为:
# 此处可以写绝对路径也可以写相对路径
. "./Write-Log.ps1" Write-Log -Message 'It's a test message'
函数自身会创建一个上下文(scope),在函数中定义的变量在函数外部是不可见的。
但是在函数内部却可以访问在函数外部定义的变量,比如下面的 demo:
$world = "Hello world!" function Say-Hello
{ Write-Host($world) }
> Say-Hello