此刻,你已经学到如何高效使用PowerShell的管道。这些命令的功能非常强大(比如Get-Process | Sort VM-desc | ConvertTo-HTML | Out-File process.html)。如果采用其他脚本语言实现相同功能,可能需要编写多行代码,但是利用PowerShell,仅需要单行命令即可。但是,你本可以做得更好。在本章中,我们会更深入地讲解管道相关的知识,并展示其更加强大的功能。
9.1 管道:更少的输入,更强大的功能
我们喜欢PowerShell的一个重要原因是它并不像VBScript那样,需要我们写很多的代码来实现某些功能,从而使得我们的工作更加高效。单行的PowerShell命令功能如此强大,主要在于PowerShell管道的工作机制。
另外需要说明:你完全可以跳过本章的学习,也可以高效使用PowerShell。但是在大部分情况下,你不得不采用VBScript的风格编写脚本或者程序。虽然PowerShell的管道功能非常复杂,但是相比于其他更为复杂的编程语言,它更容易学习。通过学习如何使用管道,你可以更高效地完成某项工作,而无须编写脚本。
本章的宗旨是在尽量键入较少的命令的前提下,如何让Shell完成更多的工作。可以想象,你会很惊讶地发现,该Shell可以非常完美地实现这些功能。
9.2 PowerShell如何传输数据给管道
当将两条命令串联在一起时,PowerShell必须搞清楚怎样将第一条命令的输出作为第二条命令的输入。在下面的示例中,我们将第一条命令称为命令A,这条命令会产生某些结果。第二条命令称为命令B,它会接收命令A产生的结果集,然后完成自己的工作。
PS C:/>CommandA | CommandB
如图9.1所示,在该文本文件中,每行均代表一个计算机的名称。
图9.1 创建一个包含计算机名称的文本文件,每行代表一个计算机名称
你可能希望将这部分计算机名称作为某些命令的传入数据,以便该命令会在这些计算机上被运行,比如下面的例子。
PS C:/> Get-Content ./computers.txt | Get-Service
当运行Get-Content命令时,它会将文本文件中的计算机名称放入管道中。之后PowerShell再决定如何将该数据传递给Get-Service命令。但PowerShell一次只能使用单个参数来接收传入数据。也就是说,PowerShell必须决定由Get-Service的哪个参数来接收Get-Content的输出结果。这个决定的过程就称为管道参数绑定(Pipeline parameter binding),这也是本章主要讲解的内容。PowerShell将使用两种方法来将Get-Content的输出结果传入给Get-Service的某个参数。该Shell尝试使用的第一种方法称为ByValue;如果这种方法行不通,它将会尝试ByPropertyName。
9.3 方案A:使用ByValue进行管道输入
当使用ByValue这种方式实现管道参数绑定时,PowerShell会确认命令A产生的数据对象类型,然后查看命令B中哪个参数可以接受经由管道传来对象的类型。可以采用下面的方法来证明:通过管道将命令A的输出结果发送给Get-Member,然后就可以查到该命令产生的结果的对象类型。之后,查看命令B的详细帮助信息(例如Help Get-Service -Full),确定命令B的哪个参数可以接收ByValue管道传出的数据类型。图9.2展示了该过程。
你将会看到Get-Content命令产生的结果对象的类型是System.String(或者简称为String)。通过查询帮助信息,可以看到Get-Service中的确也存在可以从ByValue管道中接收String类型数据的参数。检查发现,可以接受String类型数据的参数是-Name。查看帮助信息,其说明为“指定要检索的服务的名称”。你可能已经发现一个问题:这并不是我们需要的——我们的文本文件中的内容,也就是String对象,是指计算机名称,并不是服务的名称。如果我们执行下面的命令,之后会得到名为SERVER2或者WIN8的服务名,肯定无法正常执行。
PS C:/> Get-Content ./computers.txt | Get-Service
图9.2 对比Get-Content的输出结果与Get-Service的输入参数
PowerShell只允许使用一个参数去接收ByValue管道返回的对象类型。也就意味着,由于-Name参数接收了来自ByValue管道返回的String类型数据,那么其他参数就无法接收该数据了。这样我们将文本文件中的计算机名称通过管道传递给Get-Service命令的希望破灭了。
在这个示例中,管道的输入可以正常工作,但是无法得到我们期望的结果。我们再看另外一个示例。在新示例中,我们能得到我们期望的结果。下面是对应的命令行:
PS C:/>Get-Process -Name note* | Stop-Process
我们将命令A的输出结果通过管道传递给Get-Member,之后查看命令B的详细帮助信息。图9.3即为之后的对比结果。
图9.3 将Get-Process输出结果绑定到Stop-Service命令的一个参数
Get-Process命令会返回类型为System.Diasnostics.Process的对象(注意:我们在该示例中限制了返回的Process的名称(名称以note开头);由于我们开启一个NotePad进程,所以执行该命令后,会返回对应的结果)。Stop-Process命令会使用-InputObject参数接收这些来自ByValue管道的进程对象。从帮助信息中得知,该参数会“停止由指定的进程对象表示的进程”。换句话说,命令A会返回一个或多个进程对象,命令B会停止(或者杀死)这些进程。
这是诠释管道参数绑定一个比较恰当的示例,同时反映了PowerShell中比较重要的一个知识点:大部分情况下,使用相同名词的命令都可以使用ByValue方式相互之间进行管道传输(比如Get-Process和Stop-Process)。
下面,我们看另外一个示例:
PS C:/>Get-Service -Name s* | Stop-Process
表面上看起来,这个命令没有任何意义。但是当我们将命令A的结果集通过管道传输给Get-Member,之后再查看命令B的详细帮助信息,那么也就如图9.4所示。
Get-Service返回了ServiceController类型的对象(准确地说,应该是System.ServiceProcess. ServiceController,但是我们可以只取最后一位的名称作为简写)。糟糕的是,Stop-Process没有一个参数可以接收ServiceController类型的对象。也就意味着,使用ByValue方式进行处理的方案失败,此时PowerShell会尝试其备选方案ByPropertyName。
图9.4 检查Get-Process的输出结果以及Stop-Process的输入参数
9.4 方案B:使用ByPropertyName进行管道传输
该方案同样需要将命令A的输出结果传递给命令B的参数。但是ByPropertyName与ByValue稍有不同。通过该方法,命令B的多个参数可以被同时使用。我们再次将命令A的输出结果传递给Get-Member,之后查看命令B的语法。图9.5展示了该结果:命令A的输出结果中一个属性的名称匹配到命令B的一个参数。
图9.5 映射属性到参数
很多人都会认为这里的原理很复杂,因此需要澄清一下,该Shell对该功能的实现其实非常简单:仅仅是寻找能够匹配参数名称的属性名称。就是这么简单,本例中属性“Name”与参数名称“-Name”相同,Shell会尝试将这两个值进行关联。
但是并不是如此简单就能实现:首先,它会检查-Name参数是否可以接收来自ByPropertyName管道的输出。通过查看详细帮助信息,就可以确定,如图9.6所示。
图9.6 确认Stop-Process的-Name参数是否可以接收ByPropertyName管道输出结果
在这个示例中,-Name参数可以接收来自ByPropertyName管道的输出结果,所以这个连接可以正常工作。神奇之处在于,与ByValue管道只能使用一个参数不同,ByPropertyName会将每个匹配的属性与参数进行关联(提供的每个参数都可以接收来自ByPropertyName管道的输出值)。在这个示例中,只有Name属性与-Name参数匹配,如图9.7所示。
从图9.7中可以看到产生了大量的错误。问题在于,Service的名称基本上都类似于ShellHWDetection和SessionEnv,但是服务的可执行文件一般为类似svchost.exe的这种命名规则。Stop-Process只会处理那些可执行文件的名字。虽然Name属性能通过管道关联到-Name参数,但是Name属性中隐藏的属性值并不能被-Name参数所处理,最终也就导致了上面的错误。
图9.7 尝试将Get-Service的输出结果通过管道传送给Stop-Process
下面看一个可以正常运行的示例:使用记事本新建一个以逗号间隔的CSV文件,如图9.8所示。
将该文件保存为Alias.CSV,之后回到Shell界面,尝试导入该文件,如图9.9所示。当然,你也可以将Import-CSV的输出结果通过管道传递给Get-Member,这样就可以查看输出的内容。
图9.8 在Windows记事本中新建CSV文件
图9.9 导入CSV文件,并查看它的成员
你可以清晰地看到,CSV文件中的列名成了属性,而CSV中每一行的值成了一个对象。现在我们查看New-Alias的详细帮助,如图9.10所示。
图9.10 匹配属性与对应的参数
Name和Value属性都可以关联到New-Alias的参数名称。当然,这里是特意实现的(因为你可以将CSV文件的列任意命名)。现在我们可以检查New-Alias的-Name和-Value参数是否可以接收来自ByPropertyName管道的输出结果,如图9.11所示。
经过查看,两个参数都可以接收,也就证明下面的语句可以正常工作。尝试执行下面的语句。
PS C:/>Import-CSV ./aliases.csv | New-Alias
执行之后,会产生三个新的别名,名为d、sel和go,分别对应Get-ChildItem、Select-Object和Invoke-Command命令。从这里可以看出,这是一个非常强大的功能,它可以将数据从一个命令传递给另外一个命令,之后只需要使用少量的命令语句就可以实现复杂的功能。
图9.11 寻找能接受ByPropertyName管道输入的参数
9.5 数据不对齐时:自定义属性
当我们人为创建某些输入数据时,使用CSV是非常简单的一个场景,因为我们可以人为将属性和参数名称对齐。但是当你必须通过PowerShell处理其他对象或者他人提供的数据时,可能就会变得比较困难。
比如这个示例:我们会介绍一个之前未使用过的命令New-ADUser。该命令属于活动目录中的一个模块,它存在于Windows Server 2008 R2及之后的版本操作系统的域控制器中。另外,你也可以在安装了微软的远程服务器管理工具(Remote Server Administration Tools,RSAT)的客户端电脑上找到该组件。现在请不要担心如何去运行命令,只需要跟随下面的示例就可以了。
New-ADUser命令包含大量参数,每个参数用来匹配一个新的活动目录账号的信息,比如:
- -Name(该参数必须存在)
- -samAccountName(从语法角度,可以不提供。但是仍然需要提供该参数,使得AD账号可用)
- -Department
- -City
- -Title
我们这里本可介绍更多参数,但是如果仅为练习,上面这些参数已经足够。这些参数都可以按照ByPropertyName方式接收管道的输出。
比如下面的例子,你需要处理一个CSV文件,但是该文件来自于公司的HR部门。你可能多次要求他们按照某特定格式给出文件,但是HR部门仍然固执地使用自己的文件格式,如图9.12所示。
图9.12 处理HR部门提供的CSV文件
如图9.12所示,PowerShell成功导入该CSV文件,最终产生了三个对象,并且每个对象包含四个属性。但是存在一个问题:dept属性与New-ADUser的-Department参数并不吻合;同时Login属性是无意义的,这里并没有包含samAccountName或者Name属性(如果你想通过下面的命令来创建新用户,那么必须指定这两个属性)。
PS C:/>Import-CSV ./NewUsers.CSV | New-AdUser
那么我们如何解决这个问题?当然,你可以直接打开这个CSV文件,之后修复它(将列名修改为符合New-ADUser中参数的名称),但是需要花费一定的时间去完成。PowerShell的宗旨在于减少手工劳动。为什么不通过Shell脚本来解决该问题?来看下面的示例:
PS C:/> Import-CSV ./NewUsers.CSV | >> Select-Object -Property *, >> @{name='samAccountName';expression={$_.login}}, >> @{label='Name';expression={$_.login}}, >> @{n='Department';e={$_.Dept}} >> Login : DonJ Dept : IT City : Las Vegas Title : CTO SamAccountName : DonJ Name : DonJ Department : IT Login : GregS Dept : Custodial City : Denver Title : Janitor samAccountName : GregS Name : GregS Department : Custodial Login : JeffH Dept : IT City : SyracuseTitle : NetWork Engineer SamAccountName : JeffH Name : JeffH Department : IT
看起来,语法比较特别。下面将这部分语法拆开来看:
- 这里我们使用了Select-Object命令以及它的-Property参数。最开始,我们指定了*这个属性(*是指“所有存在的属性”)。在*后面,我们使用了逗号,也就意味着我们还会输入其他的一些属性列。
- 之后我们创建一个哈希表,哈希表的结构是以@{为起始,以}为结尾。哈希表中包含了一个或者多个成对的键-值(Key-Value)数据。我们使用Select-Object去寻找我们指定的一些特定键。
- Select-Object需要寻找的第一个键可以是Name、N、Label或者L,该键对应的值也就是我们想创建的属性的名称。在第一个哈希表中,我们指定了samAccountName,第二个哈希表中为Name,第三个哈希表中指定为Department。这三个属性的名称正好可以对应到New-ADUser命令的三个参数。
- Select-Object需要的第二个键可以是expression或者E。该键对应的值是一个包含在大括号{}中的脚本块。在脚本块中,使用特定的$_占位符关联到已存在的管道对象(CSV文件中每行的数据)。通过$_可以读取管道对象的属性,或者说是CSV文件的一个列。也就是说,通过这种方法来指定新属性的值。
动手实验: 请参照图9.12新建一个CSV文件,之后输入上面示例中运行的所有命令。
到现在为止,已完成的步骤包括获取CSV文件的内容(Import-CSV的输出结果),之后在管道中动态地修改该内容。最后新的数据输出结构能与New-ADUser命令期望的格式一致,这样我们就可以使用下面的命令来创建新的AD用户了。
PS C:/> Import-CSV ./NewUsers.CSV | >> Select-Object -Property *, >> @{name='samAccountName';expression={$_.login}}, >> @{label='Name';expression={$_.login}}, >> @{n='Department';e={$_.Dept}} | >> New-ADUser >>
从语法上看,可能不是那么友好,但是确实是一门功能非常强大的技术。在PowerShell的其他地方也可以使用该命令,后续章节中会有类似示例。甚至你可以在PowerShell的帮助文件的示例中看到这种命令:执行Help Select –Example命令就可以发现,但是需要自行查看。
9.6 括号命令
有些时候,不管我们怎么尝试,都无法处理管道的输出结果,比如Get-WMIObject。下一章节中会详细讲解该命令,但是我们现在可以先大概看一下它的帮助信息,如图9.13所示。
该参数并不能接收来自管道的计算机名称。那么我们应该如何将其他来源的数据(比如一个文本文件,其中每行数据代表一个计算机名称)传递给该命令呢?如果按照下面这样编写命令,那么是不能正常执行的。
PS C:/>Get-Content ./computers.txt |Get-WMIObject -Class win32_bios
图9.13 查看Get-WMIObject的详细帮助信息
Get-Content命令输出的String对象无法匹配到Get-WMIObject命令的-ComputerName参数。那么此时,我们应该怎么做?使用圆括号。
PS C:/> Get-WMIObject -Class Win32_BIOS -ComputerName (Get-Content ./computers.txt)
现在我们回想一下高中代数课中对括号的解释:“优先执行”。也就是说,PowerShell会采用如下顺序来执行这个命令:先执行括号里的命令;第一步命令执行的结果(在本例中,是多个String类型的对象)被传递给Get-WMIObject的参数。由于-ComputerName能够接收String类型的对象,所以此时,整个命令可以正常执行。
动手实验: 如果有大量的计算机可以用来做测试,那样最好不过了。将正确的机器名和IP地址写入到一个computers.txt文件中。如果是在域环境中(在域环境中,计算机的权限变更会非常容易),那么会测试得更顺利。
括号命令功能非常强大,因为它根本不依赖于参数管道绑定——它会将获取的对象强制匹配到正确的参数。但是如果括号中输出的对象类型和需要绑定的参数类型不一致,也会存在问题。此时,我们需要手动做一些修改。详见下一小节。
9.7 提取属性的值
在本章开始展示了一个示例,在该示例中,我们使用圆括号得到Get-Content的输出结果,之后将该输出结果传递给另外一个Cmdlet的参数。
Get-Service -computerName (Get-Content names.txt)
在很多时候,我们可能不会从一个静态文件中获取计算机名称,比如可能需要从活动目录中获取某些数据。借助于ActiveDirectory模块(在Windows Server 2008 R2及之后版本操作系统上,以及在安装了远程服务器管理工具RSAT的客户端电脑上),我们可以查询域控制服务器(Domain Controller)上所有的信息。
PS C:/>Get-ADComputer -Filter * -SearchBase "ou=domain controllers, ➥dc=company,dc=pri"
你可以使用括号将上面命令的输出结果传递给Get-Service吗?也就是说,下面的命令可以执行吗?
PS C:/>Get-Service -computerName (Get-ADComputer -filter *➥-searchBase "ou=domain controllers,dc=company,dc=pri")
补充说明
如果你没有域控制器环境,那么也没问题。我们会告诉你需要了解Get-ADComputer的哪些信息。
首先,该命令包含在一个名为ActiveDirectory的模块中。正如前文提到的,在Windows 2008 Server R2以及之后版本操作系统的域控制服务器上,或者在域中某一台已经安装RSAT的客户端计算机上都存在该模块。
其次,正如你猜测的那样,该命令会获取域中的计算机对象。
再次,该命令包含两个非常有用的参数。-Filter *将会去到所有计算机上获取对应信息。当然,你也可以指定其他筛选条件来限制返回的结果(比如指定一个特定的计算机名称)。-SearchBase参数会告诉这个命令从哪个地方开始查找计算机。在上面的示例中,我们设定该命令从Company.com域的域控制器开始查找。
Get-ADComputer -Filter * -SearchBase "ou=domain➥controllers,dc=company,dc=pri"
最后,计算机对象中包含Name这个属性,也就是计算机的名称。
我们意识到,直接将这类命令(非常依赖于实验环境)教给你,你可能没法进行测试。从某种程度上说,对你来说可能不太公平。但是在生产环境中,如果真正遇到我们假设的这种场景,该命令会非常有用。如果你能记住前面讲的四点,本节的知识对你将会非常有帮助。
很遗憾,上面的命令无法成功执行。查看Get-Service的帮助文件,你可以看 到-Computer这个参数只能接收String类型的值。
请运行下面的命令:
Get-ADComputer -Filter * -SearchBase "ou=domain controllers, ➥dc=company, dc=pri" | gm
通过Get-Member命令,我们可以看到Get-ADComputer命令的输出结果是ADComputer类型的对象,而不是String类型的对象。所以-ComputerName这个参数不知道该如何来处理这部分数据。 但是ADComputer类型的对象包含了一个-Name的属性。接下来我们要做的是,提取出ADComputer类型对象中的-Name属性值,然后将这些值(也就是计算机名称)传递给-ComputerName这个参数。
提示:
这是PowerShell中很重要的一个知识点。如果你还感到不理解或者困惑,那么请停下来重新阅读前文。我们可以通过Get-Member命令来确认Get-ADComputer命令输出的是ADComputer类型的对象;但是查看帮助文档,-ComputerName这个参数只能接收String类型的对象,而无法处理ADComputer类型对象。因此,前面那个包含括号的命令无法正常执行。
再次提醒,我们可以使用Select-Object命令来解决这个问题,因为它包含一个可以接收属性名称的参数-ExpandProperty。它会获取对应的属性,提取属性的值,然后返回这些值(作为Select-Object的输出结果)。参考下面这个命令:
Get-ADComputer -Filter * -SearchBase "ou=domain controllers, ➥dc=company, dc=pri" | Select-Object -expand name
该命令会返回一个包含计算机名称的清单,里面的值可以传递给Get-Service命令的-ComputerName参数(或者其他包含-ComputerName参数的一些Cmdlet)。
Get-Service -ComputerName (Get-ADComputer -Filter * ➥-SearchBase "ou=domain controllers,dc=company,dc=pri"| ➥Select-Object -Expand name)
提示:
再次申明,这是一个非常重要的概念。一般情形下,类似Select-Object –Property Name这种命令只会返回一个Name的属性(因为我们只指定了该名称)。-ComputerName参数不期望得到任意的带有-Name属性的对象;它更期望得到一个String类型的对象,因为这样会更加简单。-ExpandName会获取Name属性,并且提取其值,最终该命令会输出一些比较简单的String对象。
最后说明一下,这是一个非常棒的技巧,可以将多种命令相互关联。这样可以避免不必要的输入,使得PowerShell可以实现更多的功能。
既然你已经看到使用Get-ADComputer的一些强大功能,下面看另外一个你可以完成的示例。假定你运行的是新版本的操作系统,在这个示例中,不需要计算机在域中,也不需要能访问到域控制服务器,甚至不需要服务器版的操作系统。我们要求得到计算机名称,因为该命令在所有命令中比较常见。
首先,在记事本中创建一个CSV文件,如图9.14所示。如果你在CSV文件中指定的计算机都可以被访问到,那么就可以正常运行示例中的命令。当然,如果只能访问到本机,在HostName列全部写为LocalHost,然后在记事本中写上3次或者4次,最后也可以正常执行该命令。
图9.14 确定可以使用Import-CSV导入该CSV文件,得出如图的类似结果
现在我们可以从列出的这部分计算机上找到正在运行的进程列表。通过查看Get-Process命令的帮助文件,你会发现它的-ComputerName参数可以接收ByPropertyName管道的输入。可接收的对象类型为String,这里我们不会关注管道输入。相反,我们关注属性的提取操作。帮助文件中显示-ComputerName参数需要String类型的对象。
图9.15 验证-ComputerName参数支持的数据类型
回到之前起始部分,我们可以将执行结果通过管道传给Get-Member来展现命令A的输出结果。图9.16显示了这个结果。
图9.16 Import-CSV命令产生PSCustomObject类型对象
Import-CSV的PSCustomObject类型输出并不是String,所以下的命令无法被执行。
PS C:/> Get-Process -ComputerName (Import-CSV ./Computers.CSV)
之后尝试从CSV文件中读取出HostName列,然后查看其输出结果,如图9.17所示。
图9.17 选择单个属性,结果仍然是PSCustomObject类型
你得到了一个PSCustomObject类型对象。相比于之前的结果,它包含更少的属性。这也是Select-Object和-Property参数的一个特点。它并不会真正影响输出整个对象的行为。
但是-ComputerName这个参数的不会处理PSCustomObject对象,所以下面的这个命令仍然无法正常运行。
PS C:/> Get-Process -ComputerName (Import-CSV ./Computers.CSV |Select -Property HostName)
这也就使得-ExpandProperty参数有了用武之地。然后尝试加上该参数,并查看该命令执行的结果,如图9.18所示。
因为HostName属性中包含文本字符串,-ExpandProperty参数就可以将这部分值放入到一些简单的String对象中去,之后-ComputerName参数就可以处理这部分值了。翻译成脚本语言,如下所示:
PS C:/>Get-Process -ComputerName (Import-CSV ./Computers.CSV |Select -Expand HostName)
该技术功能非常强大。刚接触时,可能比较难以掌握,但是如果意识到一个属性是类似于盒子的概念,这将有助于我们掌握该技术。当使用Select-Property的时候,就会确定需要使用哪个盒子,但是也只是获取到盒子而已。当使用Select -ExpandProperty时,你就可以打开对应盒子,提取里面的内容,最后扔掉整个盒子,仅保留需要的内容。
图9.18 最终得到一个String类型的对象
9.8 动手实验
注意:
在本章实验环境,需要运行3.0版本的PowerShell或者之后版本的计算机。
再次提醒大家,在本章很短的时间内,我们讲解了很多重要的概念。巩固这些新学知识最好的办法就是立即使用它们。我们建议按照顺序依次完成下面的任务,因为这些任务逐层依赖,可以帮助我们复习学到的知识点,并且能帮助我们找到如何实践学到的这些知识。
为了让实验环节更有挑战性,我们强烈建议你测试Get-ADComputer命令。任何安装了Windows Server 2008 R2或者之后版本操作系统的域控制服务器都有默认安装该命令,但是实际上,在该环节,并不需要。你只需要了解到下面三点即可:
- Get-ADComputer命令包含一个-Filter参数;运行Get-ADComputer-Filter*会返回所在域中所有的计算机对象。
- 域中计算机对象都包含一个Name属性,该属性包含了计算机的名称信息。
- 域中计算机对象都会返回一个名为ADComputer的类型名称,也就是说,Get-ADComputer命令会返回ADComputer类型的对象。
这是你应该知道的三个知识点。请记住这几点,然后完成下面的任务。
注意:
我们并不会要求你真正去运行这些命令;相反,你需要判断这些命令是否可以正常执行,如果不能正常执行,请给出对应的原因说明。前面章节已经介绍了Get-ADComputer命令是如何工作的,然后该命令会返回何种类型的对象。你也可以通过帮助文件来查看其他命令可以处理的对象。
1.下面的命令是否可以获取特定域中所有计算机上已经安装的Hotfix的清单?同时,请参照本章开头的格式,阐述其原因。
Get-HotFix -ComputerName (Get-ADComputer -Filter * |Select-Object -Expand Name)
2.下面的命令是否可以从相同计算机上获取到HotFix列表呢?同时,请参照本章开头的格式,阐述其原因。
Get-ADComputer -Filter * |Get-HotFix
3.下面第三个版本的命令是否可以获取到域中计算机上已经安装的HotFix清单?同时,请参照本章开头的格式,阐述其原因。
Get-ADComputer -Filter * |Select-Object @{l='ComputerName';e={$_.Name}} |Get-HotFix
4.使用管道参数绑定来写一个命令获取域中每一台计算机上正在运行的进程的清单。不要使用括号。
5.可以使用括号而不要使用管道输入方法来获取域中每一台计算机上已经安装的服务清单。
6.微软有些时候可能忘记给一个Cmdlet添加管道参数绑定。例如,下面的命令是否可以获取域中每台计算机上的信息?请参照本章开头的格式,阐述其原因。
Get-ADComputer -Filter * | Select-Object @{l='ComputerName';e={$_.Name}} |Get-WMIObject -Class Win32_BIOS
9.9 进一步学习
我们看到很多同学很难理解管道输入概念,主要是因为这个概念比较抽象。如果你觉得自己也是如此,那么请参考MoreLunches.Com网站。根据本书的封面或者名字去寻找,之后单击打开。找到“下载资源”部分,然后单独下载管道输入手册。将该手册打印多份,拿着一支笔,然后查看其中示例(比如Get-Service | Stop-Service)。该工作手册有逐步讲解整个管道输入流程的每一步。