PowerShell 技能连载 - 用队列代替嵌套
与其使用递归函数,您可能会希望使用一个 Queue
对象,这样在加载新的任务时可以卸载已处理的数据。
Lee Homes 最近贴出了以下示例,它不使用递归调用的方式而搜索了整个文件系统的文件夹树:
1 | # create a new queue |
try-catch
语句块是必要的,因为当没有文件或文件夹权限时,.NET 方法会抛出异常。
与其使用递归函数,您可能会希望使用一个 Queue
对象,这样在加载新的任务时可以卸载已处理的数据。
Lee Homes 最近贴出了以下示例,它不使用递归调用的方式而搜索了整个文件系统的文件夹树:
1 | # create a new queue |
try-catch
语句块是必要的,因为当没有文件或文件夹权限时,.NET 方法会抛出异常。
Get-Service
可以提供 Windows 服务的基础信息但是并不会列出所需要的特权。以下是一段简短的 PowerShell 函数,输入一个服务名并返回服务特权:
1 | function Get-ServicePrivilege |
在前一个技能中我们试验了用 Set-PSBreakpoint
在 PowerShell 中创建动态变量断点。我们演示了当一个变量改变时,如何触发一个断点。
然而,如果您希望监视对象的属性呢?假设您希望监视数组的大小,当数组元素变多时自动进入调试器。
在这个场景中,PowerShell 变量并没有改变。实际上是变量中的对象发生了改变。所以您需要一个“读”模式的断点而不是一个“写”模式的断点:
1 | # break when $array’s length is greater than 10 |
当 $array
数组的元素超过 10 个时,脚本会中断下来并进入调试器。别忘了按 SHIFT
+F5
退出调试器。
在调试过程中,变量断点可能非常有用。当一个变量改变时,变量断点能够自动生效并进入调试器。如果您知道当异常发生时某个变量变为某个设置的值(或是 NULL 值),那么可以让调试器只在那个时候介入。
以下例子演示如何使用变量断点。最好在脚本的顶部定义它们,因为您可以用 $PSCommandPath
来检验断点所需要的实际脚本文件路径:
1 | # initialize variable breakpoints (once) |
请确保执行之前先保存脚本:调试始终需要一个物理文件。
如您所见,当变量 $a
被赋予一个大于 10 的值时,调试器会自动中断下来。您可以使用 “exit
“ 命令继续,用 “?
“ 查看所有调试器选项,并且按 SHIFT
+F4
停止。
要移除所有断点,运行这行代码:
1 | PS C:\> Get-PSBreakpoint | Remove-PSBreakpoint |
默认情况下,PowerShell 会精简对象并且只显示最重要的属性:
1 | PS C:\> Get-WmiObject -Class Win32_BIOS |
要查看真实的信息,需要使用 Select-Object
并显示要求显示所有信息:
1 | PS C:\> Get-WmiObject -Class Win32_BIOS | Select-Object -Property * |
如何在自己的 PowerShell 函数中实现相同的内容并且返回自己的对象?
只需要告诉 PowerShell 缺省情况下需要可见的最重要的属性。以下是一个示例。Get-Info
函数创建一个有五个属性的自定义对象。在函数返回这个对象之前,它使用一些 PowerShell 的魔法对这个对象进行标记并且列出缺省的属性:
1 | function Get-Info |
以下是执行结果:
1 | PS C:\> Get-Info |
PowerShell 可以通过 C# 形式的代码操作底层 API。通过这种方法,可以在内存中编译 API 函数并添加新类型。以下例子使用一个 API 函数来锁定工作站:
1 | Function Lock-WorkStation |
要锁定当前用户,请运行以下代码:
1 | PS C:\> Lock-WorkStation |
当您在 PowerShell 中键入一条命令,引擎将触发三个事件来发现您想执行的命令。这为您提供了许多机会来拦截并改变命令的发现机制。让我们教 PowerShell 当在命令中加入 >>
时将命令输出结果发送到 Out-GridView
!
一下是代码:
1 | $ExecutionContext.InvokeCommand.PreCommandLookupAction = { |
接下来,输入两条命令:
1 | PS C:\> Get-Process -Id $PID |
第一条命令只是输出当前进程。第二条命令自动将执行结果输出到 Out-GridView
。
如果您希望取消这种行为,请重新启动 PowerShell(或将一个空脚本块赋值给该事件)。如果您希望使该行为永久生效,请将以上代码加入到您的 profile
脚本中。
当您在 PowerShell 键入一个命令,将触发一系列事件来指定命令所在的位置。这从 PreCommandLookupAction
开始,您可以用它来记录日志。请看如下代码:
1 | $ExecutionContext.InvokeCommand.PreCommandLookupAction = { |
当您运行这段代码,所有键入的命令都将回显到控制台中——除了白名单中列出命令。这演示了 PreCommandLookupAction
的工作方式:每当您键入一条命令时,将自动触发它,而且您也可以将命令写入一个日志文件。
在前一个技能中我们演示了一系列安全地将变量加入到字符串中的方法。将变量变量添加到双引号包围的文本中会导致
# this is the desired output:
# PowerShell Version is 5.1.17763.316
# this DOES NOT WORK:
"PowerShell Version is $PSVersionTable.PSVersion"
当您运行这段代码,输出结果并不是大多数人想象的那样。语法着色已经暗示了错误的地方:双引号括起来的字符串只会解析变量。他们不关心后续的任何信息。所以由于 $PSVersionTable
是一个哈希表对象,PowerShell 输出的是对象类型名称,然后在后面加上 “.PSVersion”:
1 | PS> "PowerShell Version is $PSVersionTable.PSVersion" |
以下是四种有效的实现:
1 | # use a subexpression |
双引号括起来的字符串可以方便地扩展变量,但是这个概念并不是万无一失的:
1 | $id = 123 |
如您所见的上述例子中,当您在双引号中放置变量时,PowerShell 自动判断变量的起止位置。而 :
被当成变量的一部分。要修复这个问题,您需要某种方法来明确地标记变量的起止位置。以下是一些修复这类问题的方法:
1 | $id = 123 |