PowerShell 技能连载 - 使用在线帮助
PowerShell 的发行版并没有带帮助文件,而本地安装帮助文件需要管理员权限。
更简单的获取帮助的方法是访问 cmdlet 的在线帮助,类似这样:
1 | Get-Help -Name Get-Acl -Online |
这将启动一个浏览器并导航到在线帮助。在线帮助通常内容更新并且更容易获得。当然,它需要 internet 连接,并且并不是每个 cmdlet 都有在线帮助版本。
PowerShell 的发行版并没有带帮助文件,而本地安装帮助文件需要管理员权限。
更简单的获取帮助的方法是访问 cmdlet 的在线帮助,类似这样:
1 | Get-Help -Name Get-Acl -Online |
这将启动一个浏览器并导航到在线帮助。在线帮助通常内容更新并且更容易获得。当然,它需要 internet 连接,并且并不是每个 cmdlet 都有在线帮助版本。
在前一个技能中我们介绍了如何记录脚本结果,以及如何使用括号来同时输出和赋值:
1 | PS> ($a = Get-Process -Id $pid) |
还可以用 -OutVariable
通用参数来实现相同的功能:
1 | PS> Get-Process -Id $pid -OutVariable b |
Tee-Object
是第三种方法:
1 | PS> Get-Process -Id $pid | Tee-Object -Variable c |
以上方法使用了管道,而管道的速度比较慢。如果希望提升性能,那么避免使用管道:
1 | PS> Tee-Object -InputObject (Get-Process -Id $pid) -Variable d |
有一系列办法能记录脚本的输出结果,但是一个非常偷懒的办法是使用 Start-Transcript
。在 PowerShell 5 中,这个 cmdlet 不仅在 powershell.exe 中支持,而且在所有宿主中都支持。所以您在 PowerShell ISE 或其它编辑器中都可以使用它。另外,transcript 支持嵌套,所以如果您写一个脚本,您可以安全地在起始处加上 Start-Transcript
并且在结尾处加上 Stop-Transcript
。
Start-Transcript
将所有输出输出写入一个文本文件。如果您没有指定路径,那么该 cmdlet 将会使用默认路径。您只需要确保脚本确实产生了可记录的输出。
要使脚本更详细,请结合这个技巧一起使用:当您将赋值语句放入一对括号 (),该赋值语句也会输出赋值的数据。这个输出结果也会被 transcript 接收到。请试试一下代码:
1 | # default assignment, no output |
当您想通过 PowerShellGet 库为所有用户安装模块,您需要管理员权限。在 Linux 的 PowerShell Core 中,您可以用 sudo
命令来启用管理员权限,并且运行 PowerShell。只需要把命令写在在大括号中即可。
在 Linux 的 PowerShell Core 上,以下命令将为所有用户从 PowerShell Gallery 中安装 AzureRM.NetCore:
1 | sudo powershell -Command {Install-Module -Name AzureRM.Netcore} |
当从 PowerShell 或 PowerShell Core 中调用 powershell.exe 时有一些小秘密:当您执行 powershell.exe
并通过 -Command
传递一些命令时,PowerShell 将运行这个命令并返回纯文本:
1 | $a = powershell -noprofile -Command Get-Service |
所以当您将代码放在大括号中执行时,PowerShell 会将强类型的结果序列化后返回:
1 | $a = powershell -noprofile -Command { Get-Service } |
完全限定名是字符串的格式,而字符串包含丰富的处理数据方法。最强大而又十分简单的是 Split()
方法。
请看使用 Split()
解析完全限定名,获取最后一个元素的名称,是多么简单:
1 | $dn = 'CN=pshero010,CN=Users,DC=powershell,DC=local' |
Split()
总是返回一个字符串数组。通过大括号,您可以操作每一个独立的数组元素。所以这段代码首先用逗号分割,然后取出第一个元素,即“CN=pshero010”。然后,再次使用相同的技术,用“=”分割。在这里,我们关心的是最后一个数组元素。PowerShell 支持负的数组下标,它从数组的尾部开始计算,所以下标为 -1 获取的是最后一个数组元素。任务完成!
您是否想知道当您不在的时候是否有人登录过您的 PC?在前一个技能中我们解释了如何从 Windows 安全日志中解析详细的审计信息,假设您拥有管理员权限。
To find out who logged into your PC, try the code below! The function Get-LogonInfo searches for security events with ID 4624. Security information is protected, so you need to be an Administrator to run this code. This is why the code uses a #requires statement that prevents non-Admins from running the code.
要查看谁登录到了您的 PC,请试试以下代码!Get-LogonInfo
函数搜索 ID 为 4624 的安全事件。安全信息是受保护的,所以只有管理员账户才能执行这段代码。这是为什么这段代码使用 #requires
来防止非管理员执行这段代码的原因。
1 | #requires -RunAsAdministrator |
这个函数也利用了 $PSBoundParameters
哈希表。这个哈希表包含了用户传入的所有参数。只有一部分信息需要传递给 Get-EventLog
命令,所以用于其他参数需要从哈希表中移除。这样,用户可以只传递 Before
、After
和 ComputerName
给 Get-EventLog
命令。
接下来,处理事件信息。所有相关的信息都可以在 ReplacementStrings
属性中找到。这个属性是一个数组。正如结果所展示的那样,ID 为 4624 的事件,第六个(下标为 5)元素为用户名,第七个(下标为 6)元素为域名,第十八个(下标为 17)列出执行登录操作的可执行程序路径。
物理上的登陆通常是由 lass
,即本地安全授权执行的。所以要只查看由人类执行的登录操作,请使用以下代码:
1 | $yesterday = (Get-Date).AddDays(-1) |
Windows 的“安全”日志包含了丰富的审计信息。默认情况下,它记录了所有的提权请求。当您以管理员身份运行一个应用程序的时候,就会产生一条记录。
要获取您机器上提权的记录列表,请试试以下代码:
1 | #requires -RunAsAdministrator |
Get-ElevationInfo
查询 ID 为 4672 的系统日志。安全信息是受保护的,所以只有管理员账户才能执行这段代码。这是为什么这段代码使用 #requires
来防止非管理员执行这段代码的原因。
这个函数也利用了 $PSBoundParameters
哈希表。这个哈希表包含了用户传入的所有参数。只有一部分信息需要传递给 Get-EventLog
命令,所以用于其他参数需要从哈希表中移除。这样,用户可以只传递 Before
、After
和 ComputerName
给 Get-EventLog
命令。
接下来,处理事件信息。所有相关的信息都可以在 ReplacementStrings
属性中找到。这个属性是一个数组。正如结果所展示的那样,ID 为 4672 的事件,第二个(下标为 1)元素为用户名,第三个(下标为 2)元素为域名,第五个(下标为 4)列出获取到的安全特权。
当一个 PowerShell 函数需要返回多用于一种信息时,一定要将它们打包成一个对象。只有通过这种方法,调用者才能够发现和独立存取该信息。以下是一个快速的例子。
这个函数只是输出三段数据。它以一个包含不同对象的数组形式返回:
1 | function test |
以下是一个更好的函数,能返回相同的信息,但是这些信息被封装为一个结构化的对象。通过这种方法,用户可以容易地读取函数返回的信息:
1 | function test |
如果您知道 WMI 类查询的名字,Get-WmiObject
和 Get-CimInstance
两个命令都可以提供丰富的信息。
以下是一个名为 Explore-WMI
的快速的 PowerShell 函数,它可以帮您查找有用的 WMI 类名:
1 | function Explore-WMI |
当运行完这段代码后,调用 Explore-WMI
命令,它将会打开一个 grid view 窗口,显示所有以 “Win32_” 开头的 WMI 类,并且不包括性能计数器类,并且暴露至少 6 个属性。您接下来可以选择其中一个。PowerShell 将会显示这个类的实例和它的所有数据,然后将生成这些结果的命令复制到剪贴板。
通过这种方式可以方便有趣地在 WMI 中搜索有用的信息,并且获取得到这些信息的代码。