Share via


Windows PowerShellWindows PowerShell 建構

Don Jones

上個月,我說過直接利用 Windows PowerShell 來解決管理工作的一些方法—真的不用寫任何指令碼。Windows PowerShell 是相當卓越的互動式命令介面 (Shell),利用其功能強大又簡單的指令碼語言,就可以實際發揮它的功能來自動化相當複雜的工作。

您可能一開始就自問:Microsoft 真的需要另一種指令碼語言嗎?Microsoft 畢竟已經給了我們 KiXtart (一種登入指令碼處理器) 及 Visual Basic® Scripting Edition (VBScript)。答案是:需要。Microsoft 的確需要另一種指令碼語言。我會說明原因。

Windows PowerShell™ 語言必須簡單又直覺,讓管理員可以很容易上手,不用太多訓練。也必須相當靈活,才能搭配 Windows PowerShell 本身提供給使用者的所有強大功能。

因為 Windows PowerShell 以 Microsoft® .NET Framework 為基礎,指令碼語法就必須合乎 .NET 規格。在彙編 Windows PowerShell 的指令碼語言時,設計師選擇基本上相當簡化的 C# 語法 (唸成 C-Sharp,是 .NET Framework 隨附的語言之一)。為何不堅持使用 VBScript 之類的語法呢?Windows PowerShell 指令碼語言並非完全有別於 VBScript,只是為了更接近 C# 語法,好讓 PowerShell 能夠變成一種學習 .NET Framework 程式設計的墊腳石。如果決定提升到 Visual Studio® 並開始撰寫 C# 應用程式,則您大部分的 Windows PowerShell 指令碼語法也會隨之提升。

就此而言,Windows PowerShell 指令碼語言 (或任何指令碼語言) 最重要的一件事就是「建構」。這些是特殊的語言元素,可讓 Windows PowerShell 執行邏輯比較,並根據比較結果來採取不同的動作,或是不斷地重複一個或多個指令。

邏輯思維

還輯比較是許多指令碼語言建構的核心工作,Windows PowerShell 也不例外。基本上,比較時會查看兩個值或物件,然後評估比較為 True 或 False。例如,您可以自問:「這位使用者的密碼到期日是今天嗎?」如果日期相同,則結果為 True,如果日期不同,則結果為 False。請注意,True 和 False 的字首是大寫,因為這在 Windows PowerShell 中是兩個有特殊意義的術語。

下列是在 Windows PowerShell 內可真正執行的邏輯比較範例:

PS C:\> $a = 1
PS C:\> $b = 2
PS C:\> $a -eq $b
False

我建立一個變數叫做 $a,並設為含有 1 這個值。在 Windows PowerShell 中,變數名稱一律以貨幣符號開頭,這樣可以容易認出。在技術上,= 代表指派運算子,用來指派一個值。接著,我建立第二個變數 $b,並指派 2 這個值。然後執行實際的比較,我利用 -eq (equality) 運算子要求 Windows PowerShell 去比較 $a 和 $b 的內容。PowerShell 會執行比較,判斷出這兩個值不相等,然後顯示結果:False。

Windows PowerShell 運算子與您看過的其他指令碼語言稍微不同,甚至也不同於 C#。大部分語言將使用 = 運算子來執行等式檢查和值指派;為了避免混淆,Windows PowerShell 在每一項功能上有專屬的運算子。[圖 1] 顯示 Windows PowerShell 比較運算子,同時以您可能熟悉之其他語言 (例如 VBScript) 的同等運算子來做對照。

這些比較運算子還有一項有趣的特色。請看一下這個比較:

PS C:\> $a = "TechNet"
PS C:\> $b = "technet"
PS C:\> $a -eq $b
True

依預設,比較運算子不區分大小寫,也就是說大寫字母的 TechNet 和 "technet" 會視為相等。這樣子相當方便,因為在大部分的管理工作中,您並不考慮字母的大小寫。不過有時還是會考慮大小寫,您可以在比較運算子前面加上 c 這個字母,要求 Windows PowerShell 執行區分大小寫的比較:

PS C:\> $a -ceq $b
False

同樣地,如果您很在意或搞不清楚 Windows PowerShell 是否執行區分大小寫的比較,只要在前面加字 i 這個字母就可以了:

PS C:\> $a -ieq $b
True

只要記得邏輯比較的結果一定是兩個值的其中一個:True 或 False。

決策

您現在知道如何撰寫邏輯比較,可以開始運用在建構中。我要說明的第一個建構可讓 Windows PowerShell 根據比較來做決策。就叫做 If 建構,另有一些變化形式。下面是最簡單的格式:

PS C:\> if ($a -eq $b) {
>> Write-Host "They are equal"
>> }
>>
They are equal

其中有一些值得討論的事情。首先,變數 $a 和 $b 還是分別含有 "TechNet" 和 "technet" 值。我的建構一開始就使用 If 關鍵字。接著在括號內輸入我想要執行的邏輯比較。接著是大括弧,代表將會呼叫的條件程式碼的開頭 - Windows PowerShell 在比較結果傳回 True 時將執行的程式碼。從稍早的範例中,可知這項比較確實傳回 True,所以我預期會執行條件程式碼。我鍵入條件程式碼 Write-Host "They are equal",然後按 Enter 鍵。最後,我鍵入右邊大括弧來結束條件程式碼區段,然後按兩次 Enter 鍵 (在空白行的第二個 Enter 鍵是讓剖析器知道我已經寫完,準備要執行程式碼)。

請注意,此建構並不是從指令檔中執行。而是直接在 Windows PowerShell 命令行中鍵入。這就是 Windows PowerShell 在 Windows 指令碼領域中的獨特之處:指令碼可以互動地建立,也可以放入檔案中永久保存。

當我鍵入左邊大括弧並按下 Enter 鍵時,Windows PowerShell 立刻就顯示 >> 提示符號。意思是:「我知道您在建構內,我已經準備好讓您輸入建構的任何內容。」當我鍵入右邊大括弧並按兩次 Enter 鍵之後,Windows PowerShell 就立即執行建構,並判斷出邏輯比較為 True,接著就執行條件程式碼。PowerShell 在回到正常提示之前先出現 "They are equal",您是由此得知。在 Windows PowerShell 中互動地編寫指令碼可迅速測試小段的程式碼,再組合成更永久的指令碼,所以在學習和偵錯上都很簡單。

我應該指出 Windows PowerShell 並不會特別挑剔一些像是按 Enter 鍵的動作。例如,下列程式碼和前一個範例的用途一樣:

PS C:\> if ($a -eq $b) { Write-Host "They are equal" }
They are equal

因為是全部鍵入成一行,所以 Windows PowerShell 不需要顯示特殊的 >> 提示符號,當我在行尾按下 Enter 鍵時就會直接執行建構。Windows PowerShell 怎麼知道要開始執行建構?因為鍵入右邊大括弧時就表示完成建構。

我順便提一下 If 建構的其他變化形式。下列是完整範例,這是在 PS1 指令檔中的樣子,不是在命令介面 (Shell) 中:

if ($a -eq $b) {
  Write-Host "They are equal"
} elseif ($a -lt $b) {
  Write Host "One is less than the other"
} else {
  Write Host "One is greater than the other"
}

此建構一開始也是用 If 關鍵字。不過,考量到比較為 False 的情況,我利用 Elseif 關鍵字提供另一種比較。如果第二個比較也為 False,則最後一個關鍵字 Else 就提供一組最後會執行的程式碼。

自行重複

Windows PowerShell 有幾個建構可反覆執行程式碼,直到某些比較結果為 True 或 False 為止。程式設計師可以呼叫這些迴圈建構。更好的是其中一個最有用的迴圈建構能夠列舉集合中的物件,並且為每一個物件執行一行或多行的程式碼。此建構稱為 foreach 建構實在很貼切,如下所示:

PS C:\> $names = get-content "c:\computers.txt"
PS C:\> foreach ($name in $names) {
>> Write-Host $name
>> }
>>
don-pc
testbed

我一開始要求 Windows PowerShell Get-Content cmdlet 擷取 c:\computers.txt 檔案的內容,這是我自己建立的檔案,其中每一行有一個電腦名稱。Windows PowerShell 將每一行視為一個物件,所以檔案基本上是含有這些物件的集合。此集合最後會放在變數 $names 中。我利用 Foreach 關鍵字來要求 Windows PowerShell 列舉 $names 集合,以變數 $name 來代表迴圈每一次執行時的目前物件。迴圈程式碼放在大括弧內。所以我將檔案中的每一個名稱輸出到命令行。從建構後面的輸出可以看出 Windows PowerShell 的實際作法。您可以看出這在管理指令碼時有多麼明顯的好處:例如,建構伺服器名稱的清單很簡單,並且讓 Windows PowerShell 依次擷取每一個伺服器的資訊。

實際的建構

我們來做一下邏輯比較及 If 建構和 foreach 建構,做一些有用的事情。我想要快速檢查一組伺服器的 Messenger 服務狀態。我預期此服務在大多數的伺服器上已停止,所以我不要讓 Windows PowerShell 列出此服務處於這種狀態的所有伺服器,我只要列出 Messenger 服務確實已啟動的伺服器,因為我只想要對這些伺服器做一些處理。

我知道 Windows PowerShell Get-Service cmdlet 可協助擷取我需要的本機電腦資訊。但很可惜,此 cmdlet 無法觸及遠端電腦,而這正是我要的。幸好,我可以透過 Windows Management Instrumentation (WMI) 來存取這些資訊,用的是 Get-WMIObject cmdlet,這讓我可以使用遠端電腦。指令碼如下:

$names = get-content c:\computers.txt
foreach ($name in $names) {
  $svc = get-wmiobject win32_service '
   -computer $name -filter "name='messenger'"
  if ($svc.started -eq $True ) {
    write-host "$name has Messenger running"
  }
}

注意到第三行的 ' 字元嗎?此字元向 Windows PowerShell 表示接續下一行。如果雜誌的版面太小,印出一整行時一定要換行,這時就很有用。另外請注意我的 If 建構會比較 $svc.started 是否為 $True。變數 $True 是 Windows PowerShell 中的特殊變數,代表布林值 True (比較變數 $False 代表布林值 False)。實際上可以再簡化一些:

$names = get-content c:\computers.txt
foreach ($name in $names) {
  $svc = get-wmiobject win32_service '
   -computer $name -filter "name='messenger'"
  if ($svc.started) {
    write-host "$name has Messenger running"
  }
}

記住,If 建構的條件一定是 True 或 False。通常可以比較兩個值來得到 True 或 False,就像我在這段指令碼的第一個版本中所做的一樣。不過,因為 Started 屬性不是 True 就是 False,所以實際上不需要比較是否為 True 或 False。

一個有用的工具

您已有一個簡單又實用的管理工具,可利用建構來完成工作。不論是在 Windows PowerShell 中互動地鍵入,或儲存為 PS1 檔案來重複使用,這個方便的工具可以檢查各電腦上的服務狀態,並展現建構如何自動化管理工作的威力。

Don Jones是 SAPIEN Technologies 的專案與服務總監,也是《Windows PowerShell:TFM》(SAPIEN Press, 2006) 的執筆者之一。請造訪 Don 的網站,網址是 ScriptingAnswers.com

© 2008 Microsoft Corporation and CMP Media, LLC. 保留所有權利;未經允許,嚴禁部分或全部複製.