給大家安利一下 PowerShell

2019-03-18 10:32:00

程式碼更簡潔、指令碼更清晰、跨平台一致性等好處是讓 Linux 和 OS X 使用者喜愛 PowerShell 的原因。

今年(2018)早些時候,Powershell CoreMIT 開源協定發布了正式可用版(GA)。PowerShell 算不上是新技術。自 2006 年為 Windows 發布了第一版 PowerShell 以來,PowerShell 的建立者在結合了 Unⅸ shell 的強大和靈活的同時也在彌補他們所意識到的缺點,特別是從組合命令中獲取值時所要進行的文字操作。

在發布了 5 個主要版本之後,PowerShell 已經可以在所有主流作業系統上(包括 OS X 和 Linux)本地執行同樣創新的 shell 和命令列環境。一些人(應該說是大多數人)可能依舊在嘲弄這位誕生於 Windows 的闖入者的大膽和冒失:為那些遠古以來(從千禧年開始算不算?)便存在著強大的 shell 環境的平台引薦自己。在本帖中,我希望可以將 PowerShell 的優勢介紹給大家,甚至是那些經驗老道的使用者。

跨平台一致性

如果你計劃將指令碼從一個執行環境遷移到另一個平台時,你需要確保只使用了那些在兩個平台下都起作用的命令和語法。比如在 GNU 系統中,你可以通過以下方式獲取昨天的日期:

date --date="1 day ago"

在 BSD 系統中(比如 OS X),上述語法將沒辦法工作,因為 BSD 的 date 工具需要以下語法:

date -v -1d

因為 PowerShell 具有寬鬆的許可證,並且在所有的平台都有構建,所以你可以把 PowerShell 和你的應用一起打包。因此,當你的指令碼執行在目標系統中時,它們會執行在一樣的 shell 環境中,使用與你的測試環境中同樣的命令實現。

物件和結構化資料

*nix 命令和工具依賴於你使用和操控非結構化資料的能力。對於那些長期活在 sedgrepawk 環境下的人們來說,這可能是小菜一碟,但現在有更好的選擇。

讓我們使用 PowerShell 重寫那個獲取昨天日期的範例。為了獲取當前日期,使用 Get-Date cmdlet(讀作 “commandlet”):

> Get-Date                        Sunday, January 21, 2018 8:12:41 PM

你所看到的輸出實際上並不是一個文字字串。不如說,這是 .Net Core 物件的一個字串表現形式。就像任何 OOP 環境中的物件一樣,它具有型別以及你可以呼叫的方法。

讓我們來證明這一點:

> $(Get-Date).GetType().FullNameSystem.DateTime

$(...) 語法就像你所期望的 POSIX shell 中那樣,計算括弧中的命令然後替換整個表示式。但是在 PowerShell 中,這種表示式中的 $ 是可選的。並且,最重要的是,結果是一個 .Net 物件,而不是文字。因此我們可以呼叫該物件中的 GetType() 方法來獲取該物件型別(類似於 Java 中的 Class 物件),FullName 屬性 則用來獲取該型別的全稱。

那麼,這種物件導向的 shell 是如何讓你的工作變得更加簡單呢?

首先,你可將任何物件排進 Get-Member cmdlet 來檢視它提供的所有方法和屬性。

> (Get-Date) | Get-MemberPS /home/yevster/Documents/ArticlesInProgress> $(Get-Date) | Get-Member            TypeName: System.DateTimeName                 MemberType     Definition                                 ----                 ----------     ----------                                 Add                  Method         datetime Add(timespan value)               AddDays              Method         datetime AddDays(double value)             AddHours             Method         datetime AddHours(double value)            AddMilliseconds      Method         datetime AddMilliseconds(double value)     AddMinutes           Method         datetime AddMinutes(double value)          AddMonths            Method         datetime AddMonths(int months)             AddSeconds           Method         datetime AddSeconds(double value)          AddTicks             Method         datetime AddTicks(long value)              AddYears             Method         datetime AddYears(int value)               CompareTo            Method         int CompareTo(System.Object value), int ...

你可以很快的看到 DateTime 物件具有一個 AddDays 方法,從而可以使用它來快速的獲取昨天的日期:

> (Get-Date).AddDays(-1)Saturday, January 20, 2018 8:24:42 PM

為了做一些更刺激的事,讓我們呼叫 Yahoo 的天氣服務(因為它不需要 API 令牌)然後獲取你的本地天氣。

$city="Boston"$state="MA"$url="https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22${city}%2C%20${state}%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"

現在,我們可以使用老派的方法然後直接執行 curl $url 來獲取 JSON 二進位制物件,或者……

$weather=(Invoke-RestMethod $url)

如果你檢視了 $weather 型別(執行 echo $weather.GetType().FullName),你將會發現它是一個 PSCustomObject。這是一個用來反射 JSON 結構的動態物件。

然後 PowerShell 可以通過 tab 補齊來幫助你完成命令輸入。只需要輸入 $weather.(確報包含了 .)然後按下 Tab 鍵。你將看到所有根級別的 JSON 鍵。輸入其中的一個,然後跟上 . ,再一次按下 Tab 鍵,你將看到它所有的子鍵(如果有的話)。

因此,你可以輕易的導航到你所想要的資料:

> echo $weather.query.results.channel.atmosphere.pressure                         1019.0> echo $weather.query.results.channel.wind.chill                                 41

並且如果你有非結構化的 JSON 或 CSV 資料(通過外部命令返回的),只需要將它相應的排進 ConverFrom-JsonConvertFrom-CSV cmdlet,然後你可以得到一個漂亮乾淨的物件。

計算 vs. 自動化

我們使用 shell 用於兩種目的。一個是用於計算,執行獨立的命令然後手動響應它們的輸出。另一個是自動化,通過寫指令碼執行多個命令,然後以程式設計的方式相應它們的輸出。

我們大多數人都能發現這兩種目的在 shell 上的不同且互相衝突的要求。計算任務要求 shell 簡潔明瞭。使用者輸入的越少越好。但如果使用者輸入對其他使用者來說幾乎難以理解,那這一點就不重要了。指令碼,從另一個角度來講是程式碼。可讀性和可維護性是關鍵。這一方面,POSIX 工具通常是失敗的。雖然一些命令通常會為它們的引數提供簡潔明瞭的語法(如:-f--force),但是命令名字本身就不簡潔明瞭。

PowerShell 提供了幾個機制來消除這種浮士德式的平衡。

首先,tab 補齊可以消除鍵入引數名的需要。比如:鍵入 Get-Random -Mi,按下 Tab 然後 PowerShell 將會為你完成引數:Get-Random -Minimum。但是如果你想更簡潔一些,你甚至不需要按下 Tab。如下所示,PowerShell 可以理解:

Get-Random -Mi 1 -Ma 10

因為 MiMa 每一個都具有獨立不同的補齊。

你可能已經留意到所有的 PowerShell cmdlet 名稱具有動名詞結構。這有助於指令碼的可讀性,但是你可能不想一而再、再而三的鍵入 Get-。所以並不需要!如果你之間鍵入了一個名詞而沒有動詞的話,PowerShell 將查詢帶有該名詞的 Get- 命令。

小心:儘管 PowerShell 不區分大小寫,但在使用 PowerShell 命令是時,名詞首字母大寫是一個好習慣。比如,鍵入 date 將會呼叫系統中的 date 工具。鍵入 Date 將會呼叫 PowerShell 的 Get-Date cmdlet。

如果這還不夠,PowerShell 還提供了別名,用來建立簡單的名字。比如,如果鍵入 alias -name cd,你將會發現 cd 在 PowerShell 實際上時 Set-Location 命令的別名。

所以回顧以下 —— 你可以使用強大的 tab 補全、別名,和名詞補全來保持命令名詞簡潔、自動化和一致性引數名截斷,與此同時還可以享受豐富、可讀的語法格式。

那麼……你看呢?

這些只是 PowerShell 的一部分優勢。還有更多特性和 cmdlet,我還沒討論(如果你想弄哭 grep 的話,可以檢視 Where-Object 或其別稱 ?)。如果你有點懷舊的話,PowerShell 可以為你載入原來的本地工具。但是給自己足夠的時間來適應 PowerShell 物件導向 cmdlet 的世界,然後你將發現自己會選擇忘記回去的路。