Powershell 管道原理分析-阿里云开发者社区

开发者社区> 余二五> 正文

Powershell 管道原理分析

简介:
+关注继续查看

豆子一直以为管道其实是很简单的,无非就是把前一个的输出结果通过管道传给下一个命令的输入嘛,貌似很多网上教程也就这么解释一下,然后就是各种演示命令了。昨天看了一个MVA2年前的powershell快速入门课程,才发现很多细节都忽略掉了,这个细节对于理解管道怎么工作是非常重要的,因为有的时候不是所有的命令都支持互相管道传输。知道了他的工作方式,才能更有效的使用管道。


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


Powershell 的管道传输有两种方式,byvalue和bypropertyname。


byvalue的意思就是输出类型和输入的类型是一样,自然可以传递。


比如我可以把get-service的结果传给stop-service,-whatif可以帮助我确认这个命令的效果,这样可以避免一些危险的操作。

1
2
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


1
2
3
4
5
6
7
8
9
10
11
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

1
PS C:\windows\system32> get-help Stop-Service -show

结果如下,他接受管道输入,而且接受类型为serviceController,因此他可以接受get-service 的输入。


wKiom1W1jsvgmQ8MAAE4HH_OwrU710.jpg



bypropertyname 的意思是如果管道的输入对象里面有一个属性,他的名字和类型都和输出命令的某一个参数的名字和类型都对的上号,那么这样的管道也是成立的。


下面看一个例子,这样执行是失败的。为什么呢,我们来分析一下

1
2
3
4
5
6
7
8
9
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的类型

1
2
3
4
5
6
7
8
9
10
11
12
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类型,对不上,因此挂了

wKiom1W1jFnQ2gJ9AAE6CB8AQ7Q798.jpg



那么我们看看bypropertyname,他接受一个管道对象的属性为computername ,类型为字符串的作为他的参数输入

wKioL1W1kVuDCvn7AAG2gPJZAq8862.jpg


看看我们的管道对象,可以看见他有一个叫做name的属性,类型为字符串。因为名字不匹配,所以管道仍然无法传输。

wKiom1W1j3Cwki6TAAMDz55c2tQ086.jpg


解决方式很简单,自定义一个属性,保证他的名字一样就行了,比如

1
2
3
4
PS C:\windows\system32> get-adcomputer sydwsus | select name, @{name="computername";expression={$_.name}}
name                                                        computername
----                                                        ------------
SYDWSUS                                                     SYDWSUS


再执行一次,就工作了

1
2
3
4
5
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的属性就好了

1
2
3
4
PS C:\windows\system32> get-adcomputer sydwsus | Get-Service -ComputerName {$_.name} bits
Status   Name               DisplayName
------   ----               -----------
Running  bits               Background Intelligent Transfer Ser...


下面再来看几个例子


我知道get-wmiobject 可以获取很多系统信息,比如

1
2
3
4
5
6
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


但是如果我通过管道执行就会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
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这个参数,这个参数拒绝接受管道的输入


wKioL1W1lInQW3MVAAHQam-3hjo390.jpg

wKiom1W1kp_CFhXWAAFPsIOyw7A221.jpg



这种不支持管道的命令,豆子的一般处理方式要么要么直接运行,要么是在管道后面用foreach的方式。


首先看看直接跑,报错

1
2
3
4
5
6
7
8
9
10
11
12
13
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输入要求是字符串


1
2
3
4
5
6
7
8
9
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


更改一下输出方式,类型就变成字符串了,再次运行就成功了


1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()
1
2
3
4
5
6
7
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以后,可以直接当作数组处理,比如下面更简洁的方式也是可以的

1
2
3
4
5
6
7
8
9
10
11
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来接受管道信息,然后对每一个对象单独执行也是可以的


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 上次重启的时间

1
2
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


wKiom1W1uxnyggAuAAJOkHrW6sk692.jpg










本文转自 beanxyz 51CTO博客,原文链接:http://blog.51cto.com/beanxyz/1678609,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
通过Windows PowerShell远程管理计算机(精简版)
现在你手中有一台server(主控端),你打算通过主控端远程管理多台server(被控端)。这个过程可以通过Windows PowerShell来完成。 首先在被控端上以管理员权限打开PowerShell,输入以下命令(其中XXX是IP中的字段,TrustedHosts填写的是主控端的IP地址。
705 0
+关注
13347
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载