杨叔叔 发表于 2018-9-2 10:51:49

Powershell 管道原理分析

  豆子一直以为管道其实是很简单的,无非就是把前一个的输出结果通过管道传给下一个命令的输入嘛,貌似很多网上教程也就这么解释一下,然后就是各种演示命令了。昨天看了一个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) , 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
  
------   ----               -----------
  
Runningbits               Background Intelligent Transfer Ser...
  当然上面的写法比较复杂繁琐,一个小技巧在 computername 后面 指定一个大括号{},他会把管道前面的整个对象结果替换过来,然后直接在里面取name的属性就好了
PS C:\windows\system32> get-adcomputer sydwsus | Get-Service -ComputerName {$_.name} bits  
Status   Name               DisplayName
  
------   ----               -----------
  
Runningbits               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: (:) , 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) , 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: (:) , 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



页: [1]
查看完整版本: Powershell 管道原理分析