豆子一直以为管道其实是很简单的,无非就是把前一个的输出结果通过管道传给下一个命令的输入嘛,貌似很多网上教程也就这么解释一下,然后就是各种演示命令了。昨天看了一个MVA2年前的powershell快速入门课程,才发现很多细节都忽略掉了,这个细节对于理解管道怎么工作是非常重要的,因为有的时候不是所有的命令都支持互相管道传输。知道了他的工作方式,才能更有效的使用管道。
下面的解释是基于Powershell V3以上的版本:
Powershell 的管道传输有两种方式,byvalue和bypropertyname。
byvalue的意思就是输出类型和输入的类型是一样,自然可以传递。
比如我可以把get-service的结果传给stop-service,-whatif可以帮助我确认这个命令的效果,这样可以避免一些危险的操作。
PS C:\windows\system32> Get-Service bits | Stop-Service -whatif What if: Performing the operation "Stop-Service" on target "Background Intelligent Transfer Service (bits)".
为什么他们可以传递呢,注意看get-service的类型是 servicecontroller
PS C:\windows\system32> Get-Service bits | gm TypeName: System.ServiceProcess.ServiceController Name MemberType Definition ---- ---------- ---------- Name AliasProperty Name = ServiceName RequiredServices AliasProperty RequiredServices = ServicesDependedOn Disposed Event System.EventHandler Disposed(System.Object, System.EventArgs) Close Method void Close() Continue Method void Continue() CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType) Dispose Method void Dispose(), void IDisposable.Dispose()
查看一下stop-service 的帮助文档 (小技巧,-show可以打开一个新的窗口),搜索byvalue
PS C:\windows\system32> get-help Stop-Service -show
结果如下,他接受管道输入,而且接受类型为serviceController,因此他可以接受get-service 的输入。
bypropertyname 的意思是如果管道的输入对象里面有一个属性,他的名字和类型都和输出命令的某一个参数的名字和类型都对的上号,那么这样的管道也是成立的。
下面看一个例子,这样执行是失败的。为什么呢,我们来分析一下
PS C:\windows\system32> get-adcomputer sydwsus | get-service bits Get-Service : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an a that is not null or empty, and then try the command again. At line:1 char:26 + get-adcomputer sydwsus | get-service bits + ~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (CN=SYDWSUS,OU=C...om,DC=com,DC=au:PSObject) [Get-Service], Paramete gValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetServiceCommand
首先看看输出类型,是一个ADComputer的类型
PS C:\windows\system32> get-adcomputer sydwsus | gm TypeName: Microsoft.ActiveDirectory.Management.ADComputer Name MemberType Definition ---- ---------- ---------- Contains Method bool Contains(string propertyName) Equals Method bool Equals(System.Object obj) GetEnumerator Method System.Collections.IDictionaryEnumerator GetEnumerator() GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Item ParameterizedProperty Microsoft.ActiveDirectory.Management.ADPropertyValueCollection Item(string p. DistinguishedName Property System.String DistinguishedName {get;set;}
查看一下get-service 的 帮助文档,byvalue需要的是serviceController类型,对不上,因此挂了
那么我们看看bypropertyname,他接受一个管道对象的属性为computername ,类型为字符串的作为他的参数输入
看看我们的管道对象,可以看见他有一个叫做name的属性,类型为字符串。因为名字不匹配,所以管道仍然无法传输。
解决方式很简单,自定义一个属性,保证他的名字一样就行了,比如
PS C:\windows\system32> get-adcomputer sydwsus | select name, @{name="computername";expression={$_.name}} name computername ---- ------------ SYDWSUS SYDWSUS
再执行一次,就工作了
PS C:\windows\system32> get-adcomputer sydwsus | select name, @{name="computername";expression={$_.name}} | Get-Service bits Status Name DisplayName ------ ---- ----------- Running bits Background Intelligent Transfer Ser...
当然上面的写法比较复杂繁琐,一个小技巧在 computername 后面 指定一个大括号{},他会把管道前面的整个对象结果替换过来,然后直接在里面取name的属性就好了
PS C:\windows\system32> get-adcomputer sydwsus | Get-Service -ComputerName {$_.name} bits Status Name DisplayName ------ ---- ----------- Running bits Background Intelligent Transfer Ser...
下面再来看几个例子
我知道get-wmiobject 可以获取很多系统信息,比如
PS C:\windows\system32> Get-WmiObject -class win32_bios SMBIOSBIOSVersion : 3.11.0950 Manufacturer : American Megatrends Inc. Name : 3.11.0950 SerialNumber : 017349452253 Version : OEMC - 300
但是如果我通过管道执行就会报错
PS C:\windows\system32> get-adcomputer sydwsus | Get-WmiObject -ComputerName {$_.name} -class win32_bios Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) At line:1 char:26 + get-adcomputer sydwsus | Get-WmiObject -ComputerName {$_.name} -class win32_bios + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand Get-WmiObject : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input. At line:1 char:26 + get-adcomputer sydwsus | Get-WmiObject -ComputerName {$_.name} -class win32_bios + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (CN=SYDWSUS,OU=C...om,DC=com,DC=au:PSObject) [Get-WmiObject], Parameter
原因很简单,这个命令既不支持byvalue,也不支持bypropertyname, 尽管他有comptuername这个参数,这个参数拒绝接受管道的输入
这种不支持管道的命令,豆子的一般处理方式要么要么直接运行,要么是在管道后面用foreach的方式。
首先看看直接跑,报错
PS C:\windows\system32> get-wmiobject win32_bios -computername (Get-ADComputer -filter{operatingsystem -like "*2008 R2*" }| select name) get-wmiobject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) At line:1 char:1 + get-wmiobject win32_bios -computername (Get-ADComputer -filter{operatingsystem - ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand get-wmiobject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) At line:1 char:1 + get-wmiobject win32_bios -computername (Get-ADComputer -filter{operatingsystem - ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
原因很简单,类型不匹配,注意这个select的输出对象仍然是ADComputer, 但是wmi的computer输入要求是字符串
PS C:\windows\system32> Get-ADComputer -filter{operatingsystem -like "*2008 R2*"}| select name | gm TypeName: Selected.Microsoft.ActiveDirectory.Management.ADComputer Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() name NoteProperty System.String name=SYDDC02
更改一下输出方式,类型就变成字符串了,再次运行就成功了
PS C:\windows\system32> Get-ADComputer -filter{operatingsystem -like "*2008 R2*"}| select -ExpandProperty name | gm TypeName: System.String Name MemberType Definition ---- ---------- ---------- Clone Method System.Object Clone(), System.Object ICloneable.Clone() CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB), int IComparab... Contains Method bool Contains(string value) CopyTo Method void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int co... EndsWith Method bool EndsWith(string value), bool EndsWith(string value, System.StringCompari... Equals Method bool Equals(System.Object obj), bool Equals(string value), bool Equals(string... GetEnumerator Method System.CharEnumerator GetEnumerator(), System.Collections.Generic.IEnumerator... GetHashCode Method int GetHashCode() GetType Method type GetType() GetTypeCode Method System.TypeCode GetTypeCode(), System.TypeCode IConvertible.GetTypeCode()
PS C:\windows\system32> get-wmiobject win32_bios -computername (Get-ADComputer -filter{operatingsystem -like "*2008 R2*" } |select -ExpandProperty name) | ft SMBIOSBIOSVersion Manufacturer Name SerialNumber Version ----------------- ------------ ---- ------------ ------- 6.00 Phoenix Technologies... PhoenixBIOS 4.0 Rele... VMware-42 1e 84 9f d... INTEL - 6040000 6.00 Phoenix Technologies... PhoenixBIOS 4.0 Rele... VMware-42 0d c3 19 1... INTEL - 6040000 6.00 Phoenix Technologies... PhoenixBIOS 4.0 Rele... VMware-42 0d ec 36 7... INTEL - 6040000
这种表达方式在2.0的时代很常见,3.0以后,可以直接当作数组处理,比如下面更简洁的方式也是可以的
PS C:\windows\system32> get-wmiobject win32_bios -computername (Get-ADComputer -filter{operatingsystem -like "*2008 R2*" }).name SMBIOSBIOSVersion : 6.00 Manufacturer : Phoenix Technologies LTD Name : PhoenixBIOS 4.0 Release 6.0 SerialNumber : VMware-42 1e 84 9f d6 f6 b5 a0-01 6d 8a c0 13 ee e6 e4 Version : INTEL - 6040000 SMBIOSBIOSVersion : 6.00 Manufacturer : Phoenix Technologies LTD Name : PhoenixBIOS 4.0 Release 6.0 SerialNumber : VMware-42 0d c3 19 1b 3a d2 43-19 36 bb c5 00 5b 69 d2
除了直接执行,通过foreach来接受管道信息,然后对每一个对象单独执行也是可以的
PS C:\windows\system32> Get-ADComputer -filter{operatingsystem -like "*2008 R2*"}| ForEach-Object{Get-WmiObject win32_bi os} SMBIOSBIOSVersion : 3.11.0950 Manufacturer : American Megatrends Inc. Name : 3.11.0950 SerialNumber : 017349452253 Version : OEMC - 300 SMBIOSBIOSVersion : 3.11.0950 Manufacturer : American Megatrends Inc. Name : 3.11.0950 SerialNumber : 017349452253 Version : OEMC - 300 SMBIOSBIOSVersion : 3.11.0950 Manufacturer : American Megatrends Inc. Name : 3.11.0950 SerialNumber : 017349452253 Version : OEMC - 300
另外,3.0以后增加了get-ciminstance 的commandlet,这个是用来替代get-wmiobject,而且他支持管道,比如, 查看所有2008 R2 上次重启的时间
PS C:\windows\system32> get-adcomputer -filter {operatingsystem -like "*2008 R2*"} | select -ExpandProperty name |Get-Ci mInstance -class win32_operatingsystem | select pscomputername,lastbootuptime | Out-GridView