天天看點

Powershell 管道原理分析

豆子一直以為管道其實是很簡單的,無非就是把前一個的輸出結果通過管道傳給下一個指令的輸入嘛,貌似很多網上教程也就這麼解釋一下,然後就是各種示範指令了。昨天看了一個MVA2年前的powershell快速入門課程,才發現很多細節都忽略掉了,這個細節對于了解管道怎麼工作是非常重要的,因為有的時候不是所有的指令都支援互相管道傳輸。知道了他的工作方式,才能更有效的使用管道。

下面的解釋是基于Powershell V3以上的版本:

Powershell 的管道傳輸有兩種方式,byvalue和bypropertyname。

byvalue的意思就是輸出類型和輸入的類型是一樣,自然可以傳遞。

比如我可以把get-service的結果傳給stop-service,-whatif可以幫助我确認這個指令的效果,這樣可以避免一些危險的操作。

1

2

<code>PS C:\windows\system32&gt; </code><code>Get-Service</code> <code>bits | </code><code>Stop-Service</code> <code>-whatif</code>

<code>What </code><code>if</code><code>: Performing the operation </code><code>"Stop-Service"</code> <code>on target </code><code>"Background Intelligent Transfer Service (bits)"</code><code>.</code>

為什麼他們可以傳遞呢,注意看get-service的類型是 servicecontroller

3

4

5

6

7

8

9

10

11

<code>PS C:\windows\system32&gt; </code><code>Get-Service</code> <code>bits | gm</code>

<code>   </code><code>TypeName: System.ServiceProcess.ServiceController</code>

<code>Name                      MemberType    Definition</code>

<code>----                      ----------    ----------</code>

<code>Name                      AliasProperty Name = ServiceName</code>

<code>RequiredServices          AliasProperty RequiredServices = ServicesDependedOn</code>

<code>Disposed                  Event         System.EventHandler Disposed(System.Object, System.EventArgs)</code>

<code>Close                     Method        void Close()</code>

<code>Continue</code>                  <code>Method        void </code><code>Continue</code><code>()</code>

<code>CreateObjRef              Method        System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)</code>

<code>Dispose                   Method        void Dispose(), void IDisposable.Dispose()</code>

檢視一下stop-service 的幫助文檔 (小技巧,-show可以打開一個新的視窗),搜尋byvalue

<code>PS C:\windows\system32&gt; </code><code>get-help</code> <code>Stop-Service</code> <code>-show</code>

結果如下,他接受管道輸入,而且接受類型為serviceController,是以他可以接受get-service 的輸入。

<a href="http://s3.51cto.com/wyfs02/M00/70/41/wKiom1W1jsvgmQ8MAAE4HH_OwrU710.jpg" target="_blank"></a>

bypropertyname 的意思是如果管道的輸入對象裡面有一個屬性,他的名字和類型都和輸出指令的某一個參數的名字和類型都對的上号,那麼這樣的管道也是成立的。

下面看一個例子,這樣執行是失敗的。為什麼呢,我們來分析一下

<code>PS C:\windows\system32&gt; </code><code>get-adcomputer</code> <code>sydwsus | </code><code>get-service</code> <code>bits</code>

<code>Get-Service</code> <code>: Cannot validate argument on </code><code>parameter</code> <code>'ComputerName'</code><code>. The argument is null or empty. Provide an a</code>

<code>that is not null or empty, and then try the command again.</code>

<code>At line:1 char:26</code>

<code>+ </code><code>get-adcomputer</code> <code>sydwsus | </code><code>get-service</code> <code>bits</code>

<code>+                          ~~~~~~~~~~~~~~~~</code>

<code>    </code><code>+ CategoryInfo          : InvalidData: (CN=SYDWSUS,OU=C...om,DC=com,DC=au:PSObject) [</code><code>Get-Service</code><code>], Paramete</code>

<code>   </code><code>gValidationException</code>

<code>    </code><code>+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetServiceCommand</code>

首先看看輸出類型,是一個ADComputer的類型

12

<code>PS C:\windows\system32&gt; </code><code>get-adcomputer</code> <code>sydwsus | gm</code>

<code>   </code><code>TypeName: Microsoft.ActiveDirectory.Management.ADComputer</code>

<code>Name              MemberType            Definition</code>

<code>----              ----------            ----------</code>

<code>Contains          Method                bool Contains(string propertyName)</code>

<code>Equals            Method                bool Equals(System.Object obj)</code>

<code>GetEnumerator     Method                System.Collections.IDictionaryEnumerator GetEnumerator()</code>

<code>GetHashCode       Method                int GetHashCode()</code>

<code>GetType           Method                type GetType()</code>

<code>ToString          Method                string ToString()</code>

<code>Item              ParameterizedProperty Microsoft.ActiveDirectory.Management.ADPropertyValueCollection Item(string p.</code>

<code>DistinguishedName Property              System.String DistinguishedName {get;set;}</code>

檢視一下get-service 的 幫助文檔,byvalue需要的是serviceController類型,對不上,是以挂了

<a href="http://s3.51cto.com/wyfs02/M02/70/41/wKiom1W1jFnQ2gJ9AAE6CB8AQ7Q798.jpg" target="_blank"></a>

那麼我們看看bypropertyname,他接受一個管道對象的屬性為computername ,類型為字元串的作為他的參數輸入

<a href="http://s3.51cto.com/wyfs02/M02/70/3F/wKioL1W1kVuDCvn7AAG2gPJZAq8862.jpg" target="_blank"></a>

看看我們的管道對象,可以看見他有一個叫做name的屬性,類型為字元串。因為名字不比對,是以管道仍然無法傳輸。

<a href="http://s3.51cto.com/wyfs02/M02/70/41/wKiom1W1j3Cwki6TAAMDz55c2tQ086.jpg" target="_blank"></a>

解決方式很簡單,自定義一個屬性,保證他的名字一樣就行了,比如

<code>PS C:\windows\system32&gt; </code><code>get-adcomputer</code> <code>sydwsus | select name, @{name=</code><code>"computername"</code><code>;expression={</code><code>$_</code><code>.name}}</code>

<code>name                                                        computername</code>

<code>----                                                        ------------</code>

<code>SYDWSUS                                                     SYDWSUS</code>

再執行一次,就工作了

<code>PS C:\windows\system32&gt; </code><code>get-adcomputer</code> <code>sydwsus | select name, @{name=</code><code>"computername"</code><code>;expression={</code><code>$_</code><code>.name}} | </code><code>Get-Service</code>

<code>bits</code>

<code>Status   Name               DisplayName</code>

<code>------   ----               -----------</code>

<code>Running  bits               Background Intelligent Transfer Ser...</code>

當然上面的寫法比較複雜繁瑣,一個小技巧在 computername 後面 指定一個大括号{},他會把管道前面的整個對象結果替換過來,然後直接在裡面取name的屬性就好了

<code>PS C:\windows\system32&gt; </code><code>get-adcomputer</code> <code>sydwsus | </code><code>Get-Service</code> <code>-ComputerName {</code><code>$_</code><code>.name} bits</code>

下面再來看幾個例子

我知道get-wmiobject 可以擷取很多系統資訊,比如

<code>PS C:\windows\system32&gt; </code><code>Get-WmiObject</code> <code>-class win32_bios</code>

<code>SMBIOSBIOSVersion : 3.11.0950</code>

<code>Manufacturer      : American Megatrends Inc.</code>

<code>Name              : 3.11.0950</code>

<code>SerialNumber      : 017349452253</code>

<code>Version           : OEMC - 300</code>

但是如果我通過管道執行就會報錯

13

<code>PS C:\windows\system32&gt; </code><code>get-adcomputer</code> <code>sydwsus | </code><code>Get-WmiObject</code> <code>-ComputerName {</code><code>$_</code><code>.name} -class win32_bios</code>

<code>Get-WmiObject</code> <code>: The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)</code>

<code>+ </code><code>get-adcomputer</code> <code>sydwsus | </code><code>Get-WmiObject</code> <code>-ComputerName {</code><code>$_</code><code>.name} -class win32_bios</code>

<code>+                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~</code>

<code>    </code><code>+ CategoryInfo          : InvalidOperation: (:) [</code><code>Get-WmiObject</code><code>], COMException</code>

<code>    </code><code>+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand</code>

<code>Get-WmiObject</code> <code>: The input object cannot be bound to any parameters </code><code>for</code> <code>the command either because the command does not</code>

<code>take pipeline input or the input and its properties </code><code>do</code> <code>not match any of the parameters that take pipeline input.</code>

<code>    </code><code>+ CategoryInfo          : InvalidArgument: (CN=SYDWSUS,OU=C...om,DC=com,DC=au:PSObject) [</code><code>Get-WmiObject</code><code>], </code><code>Parameter</code>

原因很簡單,這個指令既不支援byvalue,也不支援bypropertyname, 盡管他有comptuername這個參數,這個參數拒絕接受管道的輸入

<a href="http://s3.51cto.com/wyfs02/M00/70/3F/wKioL1W1lInQW3MVAAHQam-3hjo390.jpg" target="_blank"></a>

<a href="http://s3.51cto.com/wyfs02/M00/70/42/wKiom1W1kp_CFhXWAAFPsIOyw7A221.jpg" target="_blank"></a>

這種不支援管道的指令,豆子的一般處理方式要麼要麼直接運作,要麼是在管道後面用foreach的方式。

首先看看直接跑,報錯

<code>PS C:\windows\system32&gt; </code><code>get-wmiobject</code> <code>win32_bios -computername (</code><code>Get-ADComputer</code> <code>-</code><code>filter</code><code>{operatingsystem </code><code>-like</code> <code>"*2008 R2*"</code>

<code>}| select name)</code>

<code>get-wmiobject</code> <code>: The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)</code>

<code>At line:1 char:1</code>

<code>+ </code><code>get-wmiobject</code> <code>win32_bios -computername (</code><code>Get-ADComputer</code> <code>-</code><code>filter</code><code>{operatingsystem - ...</code>

<code>+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~</code>

原因很簡單,類型不比對,注意這個select的輸出對象仍然是ADComputer, 但是wmi的computer輸入要求是字元串

<code>PS C:\windows\system32&gt; </code><code>Get-ADComputer</code> <code>-</code><code>filter</code><code>{operatingsystem </code><code>-like</code> <code>"*2008 R2*"</code><code>}| select name | gm</code>

<code>   </code><code>TypeName: Selected.Microsoft.ActiveDirectory.Management.ADComputer</code>

<code>Name        MemberType   Definition</code>

<code>----        ----------   ----------</code>

<code>Equals      Method       bool Equals(System.Object obj)</code>

<code>GetHashCode Method       int GetHashCode()</code>

<code>GetType     Method       type GetType()</code>

<code>ToString    Method       string ToString()</code>

<code>name        NoteProperty System.String name=SYDDC02</code>

更改一下輸出方式,類型就變成字元串了,再次運作就成功了

14

<code>PS C:\windows\system32&gt; </code><code>Get-ADComputer</code> <code>-</code><code>filter</code><code>{operatingsystem </code><code>-like</code> <code>"*2008 R2*"</code><code>}| select -ExpandProperty name | gm</code>

<code>   </code><code>TypeName: System.String</code>

<code>Name             MemberType            Definition</code>

<code>----             ----------            ----------</code>

<code>Clone            Method                System.Object Clone(), System.Object ICloneable.Clone()</code>

<code>CompareTo        Method                int CompareTo(System.Object value), int CompareTo(string strB), int IComparab...</code>

<code>Contains         Method                bool Contains(string value)</code>

<code>CopyTo           Method                void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int co...</code>

<code>EndsWith         Method                bool EndsWith(string value), bool EndsWith(string value, System.StringCompari...</code>

<code>Equals           Method                bool Equals(System.Object obj), bool Equals(string value), bool Equals(string...</code>

<code>GetEnumerator    Method                System.CharEnumerator GetEnumerator(), System.Collections.Generic.IEnumerator...</code>

<code>GetHashCode      Method                int GetHashCode()</code>

<code>GetType          Method                type GetType()</code>

<code>GetTypeCode      Method                System.TypeCode GetTypeCode(), System.TypeCode IConvertible.GetTypeCode()</code>

<code>} </code><code>|select</code> <code>-ExpandProperty name) | ft</code>

<code>SMBIOSBIOSVersion       Manufacturer            Name                    SerialNumber            Version</code>

<code>-----------------       ------------            ----                    ------------            -------</code>

<code>6.00                    Phoenix Technologies... PhoenixBIOS 4.0 Rele... VMware-42 1e 84 9f d... INTEL  - 6040000</code>

<code>6.00                    Phoenix Technologies... PhoenixBIOS 4.0 Rele... VMware-42 0d c3 19 1... INTEL  - 6040000</code>

<code>6.00                    Phoenix Technologies... PhoenixBIOS 4.0 Rele... VMware-42 0d ec 36 7... INTEL  - 6040000</code>

這種表達方式在2.0的時代很常見,3.0以後,可以直接當作數組處理,比如下面更簡潔的方式也是可以的

<code>}).name</code>

<code>SMBIOSBIOSVersion : 6.00</code>

<code>Manufacturer      : Phoenix Technologies LTD</code>

<code>Name              : PhoenixBIOS 4.0 Release 6.0</code>

<code>SerialNumber      : VMware-42 1e 84 9f d6 f6 b5 a0-01 6d 8a c0 13 ee e6 e4</code>

<code>Version           : INTEL  - 6040000</code>

<code>SerialNumber      : VMware-42 0d c3 19 1b 3a d2 43-19 36 bb c5 00 5b 69 d2</code>

除了直接執行,通過foreach來接受管道資訊,然後對每一個對象單獨執行也是可以的

15

16

17

<code>PS C:\windows\system32&gt; </code><code>Get-ADComputer</code> <code>-</code><code>filter</code><code>{operatingsystem </code><code>-like</code> <code>"*2008 R2*"</code><code>}| </code><code>ForEach-Object</code><code>{</code><code>Get-WmiObject</code> <code>win32_bi</code>

<code>os}</code>

另外,3.0以後增加了get-ciminstance 的commandlet,這個是用來替代get-wmiobject,而且他支援管道,比如, 檢視所有2008 R2 上次重新開機的時間

<code>PS C:\windows\system32&gt; </code><code>get-adcomputer</code> <code>-</code><code>filter</code> <code>{operatingsystem </code><code>-like</code> <code>"*2008 R2*"</code><code>} | select -ExpandProperty name </code><code>|Get</code><code>-Ci</code>

<code>mInstance -class win32_operatingsystem  | select pscomputername,lastbootuptime | </code><code>Out-GridView</code>

<a href="http://s3.51cto.com/wyfs02/M01/70/45/wKiom1W1uxnyggAuAAJOkHrW6sk692.jpg" target="_blank"></a>

本文轉自 beanxyz 51CTO部落格,原文連結:http://blog.51cto.com/beanxyz/1678609,如需轉載請自行聯系原作者

繼續閱讀