Windows PowerShell重新探討管線

Don Jones

雖然 Windows PowerShell 是以歷史悠久的命令列介面概念為基礎,且主要使用於 UNIX 和 Linux 作業系統,但已經有太多人稱讚它的與眾不同、創新和趣味。不過 Windows PowerShell 與其前身所共通使用的名詞,卻讓人很容易就

忽略了 Windows PowerShell™ 真正的彈性與獨特性,以及它在 Windows® 環境中顯著的合適性。

Windows PowerShell 最廣為討論的一項功能,就是管線。但不幸的是,這也是經常令人誤解的一項功能。這是因為管線是源自 1970 年代早期所訂定的名詞,當時所代表的是截然不同且遜色許多的功能。

管線的由來

史上第一個 UNIX 殼層為 Thompson 殼層,只具有基本功能,而且功能僅限於最基本的指令碼語言元件,沒有變數。殼層刻意設計的十分樸素,因為其真正的目的只是要執行程式。但是其中仍提出了一項重要概念,並且改善了當時其他的殼層設計:那就是「管線」。藉由使用 < 和 > 符號,可指示殼層將輸入與輸出重新導向其他命令。例如,使用者能夠重新將命令輸出導向某個檔案。

此語法後來又繼續發展,因此一個命令的輸出可以透過管線傳遞給另一命令當做輸入,允許大量命令鏈結在一起,以完成更多複雜的工作。到了殼層的第 4 版,便採納直線字元 "|" 用於管線語法,也因此稱之為管線字元。就算是最早的 MS-DOS® 版本,只要透過這些字元實作基本管線傳遞,就能讓使用者將 Type 命令的輸出結果,傳遞給 More 命令當做輸入,因此為較長的文字檔案建立出一次顯示一頁的功能。

雖然在 1975 年推出 UNIX 第六版的時候,大多數的人都認為 Thompson 殼層不敷使用,但管線的概念卻深植入殼層開發人員與使用者心中,同時也繼續發展為現今所使用的多項技術。

以管線傳遞文字

所有殼層幾乎都有一項限制,就是以文字為基礎的本性。在 UNIX 作業系統中,這點其實不算是限制條件,而是反應出 OS 本身的運作方式。幾近所有 UNIX 中的資源都可以透過某種檔案方式呈現,也就是說,從某命令透過管線傳遞文字到另一個命令的能力,可提供極大的功能與彈性。

然而文字本身就管理資訊方面而言,絕對有其限制。例如,如果我在 Windows 電腦上顯示一張服務清單給您看,您應該都能理解其中的內容。我可能會把服務名稱放在第一欄,再把啟動模式放在第二欄。您強大的人腦可以明快、即時的將顯示的文字,剖析或解譯為可處理且有意義的資訊。然而電腦就沒那麼聰明了:如果要電腦針對該清單做出有意義的事,您必須先告訴電腦第一欄包括字元 1 到 20,而字元 22 到 40 則為第二欄,以此類推。

多年來,這種類型的文字檔案剖析,是管理員用來將多個命令鏈結在一起的唯一方法。事實上,諸如 VBScript 和 Perl 等指令碼語言的優點在於字串管理,因為他們必須能夠接受某程式或命令的文字輸出結果,並且將該輸出剖析為某種有用的資料,以便用於某些後續工作。例如,我曾寫過的 VBScript 可接受 Dir 命令的文字輸出,剖析檔案名稱和日期的輸出,接著將老舊未使用的檔案移至壓縮保存檔位置。字串剖析極難處理,因為例外 (輸入資料中的變化) 幾乎每次都會出現,需要您修改指令碼中的邏輯,以因應所有可能的變化。

字串剖析在 Windows 環境下若要當做管理指令碼或自動化使用,就比較沒有辦法發揮。這是由於 Windows 中的資訊,大多並非以便於存取的文字格式儲存。相反的,它採用以資料為中心的儲存方式,例如 Active Directory®、Windows 登錄以及憑證存放區,讓指令碼編寫者必須先使用某種工具來產生文字格式的輸出,再以指令碼剖析該文字,進而運用這些文字。

簡便的物件

Windows 軟體開發人員的日子總是比較好過。一開始,Microsoft 開發 COM 是為了以簡單好用的方式,呈現出 Windows 複雜的內部作業。現在,Microsoft® .NET Framework 仍繼續相同的工作,以標準化的方式呈現出軟體的內部作業。

一般來說,COM 和 .NET 皆以物件來顯示項目 (軟體開發人員可能會避免這樣的簡化方式,但對於本文的討論內容而言,簡單的名詞已經足夠)。這些物件全部都有不同類型的成員。為了達到目的,我們最感與趣的是物件的屬性和方法。屬性主要以某種方式描述物件,或者可修改物件本身或其行為。例如,服務物件的某個屬性可能包含服務名稱,而另一個屬性則包括服務的啟動模式。方法會致使物件採取行動。服務物件可能有名為 Stop、Start、Pause 和 Resume 等方法,用來代表您可以針對服務採取的各種行動。

從程式設計 (或指令碼) 的觀點而言,物件的成員會使用半形句點的標記格式指出。物件通常會指派給變數,以便您實際操作物件。舉例來說,如果我將服務指派給變數 $service,就可以使用語法 $service.Stop 來停止該服務。或者我可以藉由顯示 $service.Name 來擷取服務的顯示名稱。

管線中的物件

由於 Windows 是大型而複雜的作業系統,也因為它並未將管理資料以文字方式儲存,因此較舊的殼層技巧並非真正合用。例如,假設我有一個名為 SvcList.exe 的命令列工具,可產生格式化的服務與啟動模式清單。在 Windows 命令列殼層中 (該殼層穩固紮根於悠久的 MS-DOS 殼層中),我或許會執行如下指令:

SvcList.exe | MyScript.vbs 

此陳述式會擷取一份服務清單,並透過管線導向 VBScript 檔案。我必須撰寫 VBScript 檔案以剖析格式化清單,並且依我的需要執行任何動作,例如使用 Disabled 啟動模式來輸出任何服務。這項工作必須耗費不少時間。最終的問題在於 SvcList.exe 的輸出具有唯一性,並非通用格式,所以其他命令不易使用其輸出的內容。

然而,物件卻可提供通用格式,這也就是為什麼 Windows PowerShell 管線能夠處理所有物件,而不僅只於文字而已。在您執行如 Get-WMIObject 的 cmdlet 時,也就是在產生物件的群組 (就程式設計的專有名詞而言,即物件的集合)。每個物件都有完整的屬性和方法可供您操作。如果我將物件以管線傳遞至 Where-Object cmdlet,我就可以將其過濾,僅顯示出我想要的物件。Where-Object 不需要剖析任何文字,因為它不接收任何文字,只接收物件。例如:

Get-WMIObject Win32_Service | Where-Object {$_.StartMode -eq “Disabled” }

或者,如果您偏好較短的語法,可使用別名:

gwmi Win32_Service | where {$_.StartMode -eq “Disabled” }

有趣的是 Windows PowerShell 會永遠透過管線傳遞物件。殼層要到達管線末端為止 (也就是沒地方可繼續傳遞物件為止),才會使用其內建格式化規則來產生物件的文字代表。就以下面的情況舉例來說:

Gwmi Win32_Service | where {$_.StartName –eq “LocalSystem” } | select Name,StartMode

這三個 cmdlets 會從我的本機電腦擷取所有服務,過濾掉所有未使用 LocalSystem 帳戶登入的項目,接著將剩下的服務傳遞到 Select-Object cmdlet,最後僅輸出兩個我要求選取的屬性:Name 和 StartMode。輸出結果是以 LocalSystem 登入的簡單服務清單 (可用於安全性稽核目的)。

由於所有 cmdlet 皆採用共同的資料格式:物件,因此可以彼此共享資料,而無需任何複雜的字串剖析。此外,由於 Windows PowerShell 原生即可建立物件的文字代表,因此這個管線的末端一定是人類可以閱讀的文字輸出。[圖 1] 顯示已產生的輸出範例。

圖 1 透過管線傳遞物件的一組 cmdlet 所產生的文字輸出。

圖 1** 透過管線傳遞物件的一組 cmdlet 所產生的文字輸出。 **

奇妙的管線

Windows PowerShell 中的管線傳遞功能之所以如此奇妙,是因為 Windows PowerShell 內的一切都是物件,且有完整的屬性與方法可搭配使用。技術上而言,文字檔也只是一組字串物件,而檔案中的每一行都是具有唯一性,同時也是獨立的字串物件。例如,(使用 Notepad) 建立名為 C:\Computers.txt 的文字檔。在檔案中填入文字,接著在 Windows PowerShell 中執行以下動作:

Get-Content C:\Computers.txt | Select-Object Length | Format-List

或者,如果你不想打那麼多字,一樣可使用別名:

gc C:\Computers.txt | select Length | fl

此程式碼提供的清單,會指出文字檔案中每一行的字元數長度。Get-Content 會從檔案擷取字串物件,Select-Object 會找出每個物件的 Length 屬性,接著再由 Format-List 為您建立整齊、可讀的文字輸出。這或許不是很實用的系統管理工具,但足以顯示即使是一行文字這樣簡單的內容,也可以成為 Windows PowerShell 的物件。

能夠從 cmdlet 經由管線傳遞物件到另一個 cmdlet (甚至從 cmdlet 傳遞到指令碼),就可以開始建立功能強大的「單行指令碼」囉。這些簡單的 cmdlets 字串經過一系列的管線之後,即可進一步調整物件組合,以完全符合您的需求。在幾乎沒有任何指令碼或程式設計的情況下,Windows PowerShell cmdlets (在適當的管線中繫結在一起) 即可達到絕佳的成果。

支援未來的新產品

未來的 Microsoft Server 產品同樣會建立在 Windows PowerShell 基礎上,這樣的事實更能拓展這項特性。例如,在實作新的 Exchange Server 2007 電腦時,您可以使用 Windows PowerShell 來擷取所有郵件信箱,篩選過濾掉辦公室以外的並找出新郵件伺服器,接著將這些郵件信箱移動至新伺服器;上述功能只需要一行文字就能辦到,完全無須指令碼。Exchange Server 2007 工作小組發行了功能強大的單行指令碼。這些單行指令碼真正展現出管線的強大功能,及其能夠完成的系統管理工作。

若要充分運用 Windows PowerShell 的技巧,就必須了解它雖然建立在原則與觀念由來已久的 UNIX 世界,但這項新工具卻非常適用於 Windows 系統管理。不要被通用的詞彙騙了,還誤以為 Windows PowerShell 只是 Windows 在冒用 UNIX 殼層。Windows PowerShell 包含全新的概念,不但可以善加利用 Windows 平台,還能夠與各項 Windows 技巧緊密結合。

Don Jones 是 Windows PowerShell MVP,也是 Windows PowerShell 101 (ScriptingTraining.com) 的作者。他的電子郵件為 www.ScriptingAnswers.com

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