PowerShell 技能连载 - 为什么捕获不到某些错误

当您在 PowerShell 中收到一条红色的错误信息时,您总是可以用一个 try..catch 代码块将代码包裹起来,然后自行处理该错误:

try
{
  1/0
}
catch
{
  Write-Warning "Something crazy happened: $_"
}

然而,一些错误,特别是来自 cmdlet 的,并不能被处理。当那些情况发生时,说明缺失的错误是被 cmdlet 内部的错误处理器处理了,并且您可以通过 -ErrorAction 通用参数来控制 cmdlet 的错误处理器。

当您设置 ErrorAction 的值为 Stop 时,您实际上告知 cmdlet 抛出一个异常,该以上可以被您的异常处理器捕获。

要让所有 cmdlet 发出异常,而不是内部处理,您可以用 $ErrorActionPreference = 'Stop' 语句,该语句将设置所有 cmdlet 的缺省错误动作为“停止”。

请注意它的副作用:当您告知一个 cmdlet 的错误处理器要在某些错误发生时抛出异常,则该 cmdlet 将会在发生第一个错误的地方抛出异常,并且不会继续执行。

PowerShell 技能连载 - 在命令提示符中显示路径

缺省的 PowerShell 提示符显示当前的位置。当您在很深的嵌套文件夹中时,这将占用您的输入空间,而且会导致需要滚动才能看清。

有很多方法可以解决这个问题。以下是针对这个题的两个 prompt 的替代函数。

第一个是保持在当前提示符中显示当前路径,但实际的输入是在下面一行,所以您的输入总是可见。

function prompt
{
  Write-Host("PS: $pwd>")
}

另一种方式是在窗体的标题栏显示当前的路径:

function prompt
{
  $host.UI.RawUI.WindowTitle = Get-Location
  'PS> '
}

如果您想用这些方法,请将它们放在自启动脚本中(缺省情况下可能不存在)。它的路径可以通过 $profile 变量找到。

PowerShell 技能连载 - 帮助 PowerShell 做得更好!

与其批评不正常的功能,或是缺少的功能,不如一起参与建设!PowerShell 团队做了很大努力来改进和扩展 PowerShell。现在只需要您的反馈意见。

这是一站式服务地址:https://connect.microsoft.com/PowerShell

当您打开那个网页时,您可以一键提交 bug、发送反馈,或是为新的功能投票。您需要用一个免费的微软账号登录。

PowerShell 技能连载 - 试试 CTRL+SPACE!

在 PowerShell ISE 中,有两个键盘快捷键可能对您有用。按下 TAB 键的功能就像在控制台中一样,并且每次您按下 TAB,您会得到一个 Tab 展开的结果。

CTRL + SPACE 是另一个重要的键盘快捷键。它将打开智能感知菜单。由于大多数情况下 ISE 自动打开这些菜单,所以大多数用户从来没有注意过 CTRL + SPACE,但它重打开一个错过的智能感知菜单功能十分有用。

从 PowerShell 5.0 开始,CTRL + SPACE 变得更为重要,特别是当您在书写 DSC(所需状态配置)配置脚本时。PowerShell ISE 提供了针对 DSC 的扩展智能感知帮助,但在许多情况下并不会自动弹出智能感知菜单。仅当按下 CTRL + SPACE 时才能获得智能感知帮助。

PowerShell 技能连载 - 增加“命令未找到”处理器

当 PowerShell 遇到一个未知的命令名时,您会见到一条红色的信息。

然而,从 PowerShell 3.0 开始,引入了一个“CommandNotFoundHandler”功能,可以在程序中使用。它可以记录信息,或者尝试解决问题。

这是一个简单的例子。当您运行这段代码后,无论何时遇到一个 PowerShell 不知道的命令,它会运行 Show-Command 并用合法的命令打开一个帮助工具:

$ExecutionContext.InvokeCommand.CommandNotFoundAction =
{
  param(
    [string]
    $commandName,

    [System.Management.Automation.CommandLookupEventArgs]
    $eventArgs
  )

  Write-Warning "Command $commandName was not found. Opening LookilookiTool."
  $eventArgs.CommandScriptBlock = { Show-Command }

}

PowerShell 技能连载 - 自动修正 PowerShell 代码的大小写

当您编写 PowerShell 脚本时,可能常常没有使用正确的大小写,或只使用部分参数名,或使用别名而不是 cmdlet 名称。这些技术上都是可行的,因为 PowerShell 命令是大小写不敏感的,参数名可以省略,并且别名也是一种合法的命令类型。

然而,当使用正确的的大小写、完整的参数名称,以及 cmdlet 名字而不是别名时,脚本的可读性会更好。

在 PowerShell ISE 中,要纠正这些东西,只需要将光标放置在命令名或参数名处,然后按下 TAB 键。Tab 展开功能将会读取原有的代码并将它替换成大小写正确的、名字完整的版本。

PowerShell 技能连载 - 查找已加载的程序集

要列出一个 PowerShell 会话中加载的所有 .NET 程序集,请试试这段代码:

[System.AppDomain]::CurrentDomain.GetAssemblies() |
  Where-Object Location |
  Sort-Object -Property FullName |
  Select-Object -Property Name, Location, Version |
  Out-GridView

列出和对比已加载的程序集可以有助于对比 PowerShell 会话,并且检查区别。多数时间,区别在于已加载的模块,所以如果缺少了程序集,您可能需要先加载一个 PowerShell 模块来使用它们。

或者,您可以使用 Add-Type 命令根据名字或文件地址手动加载程序集。

PowerShell 技能连载 - 查找 cmdlet 参数别名

PowerShell cmdlet 和函数可以带有参数,并且这些参数可以有(更短的)别名。一个典型的例子是 -ErrorAction 通用参数,它也可以通过 -ea 别名访问。

参数别名不是自动完成的。您需要预先知道它们。以下这个脚本可以提取任意 PowerShell 函数或 cmdlet 的参数别名:

#requires -Version 3

$command = 'Get-Process'

(Get-Command $command).Parameters.Values |
  Select-Object -Property Name, Aliases

PowerShell 技能连载 - 简化参数属性

如果您的系统运行的是 PowerShell 3.0 及以上版本,您可以简化函数参数的属性。布尔属性的缺省值都为 $true,所以这在 PowerShell 2.0 中是缺省代码:

function Get-Sample
{
  Param
  (
    [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]
    $Name
  )
}

从 PowerShell 3.0 开始,它浓缩成:

function Get-Sample
{
  Param
  (
    [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [string]
    $Name
  )
}

新的代码更精炼,但是无法在 PowerShell 2.0 中运行。

PowerShell 技能连载 - 创建临时文件名

如果您只是需要创建一个临时文件名(而不是真的需要创建该文件),而且您希望控制文件的扩展名,以下是一个实现该需求的简单函数:

$elements = @()
$elements += [System.IO.Path]::GetTempPath()
$elements += [System.Guid]::NewGuid()
$elements += 'csv'


$randomPath = '{0}{1}.{2}' -f $elements
$randomPath

您可以很容易地根据它创建您自己的函数:

function New-TemporaryFileName($Extension='txt')
{
  $elements = @()
  $elements += [System.IO.Path]::GetTempPath()
  $elements += [System.Guid]::NewGuid()
  $elements += $Extension.TrimStart('.')


  '{0}{1}.{2}' -f $elements
}

这是该函数的使用方法:

PS C:\> New-TemporaryFileName
C:\Users\Tobias\AppData\Local\Temp\8d8e5001-2be8-469d-9bc8-e2e3324cce66.txt

PS C:\> New-TemporaryFileName ps1
C:\Users\Tobias\AppData\Local\Temp\412c40df-e691-44c1-8c94-f7ce30bb4875.ps1

PS C:\> New-TemporaryFileName csv
C:\Users\Tobias\AppData\Local\Temp\47b1a65f-2705-4926-8a72-21f05430f2c5.csv