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

