PowerShell 技能连载 - 高级错误处理:重新抛出异常
在处理错误时,您有时会希望将原始的异常替换成您自己的。以下是一个例子:
1 | function Do-Something |
调用者看到的内容如下:
1 | PS C:\> Do-Something |
如果调用者也用一个错误处理函数来接收它,则会出现这种情况:
1 | try |
结果看起来如下:
1 | Message Originalmessage |
这样调用者可以看到返回的错误信息,并且经过内部处理之后,还可以传递原始的错误信息。
在处理错误时,您有时会希望将原始的异常替换成您自己的。以下是一个例子:
1 | function Do-Something |
调用者看到的内容如下:
1 | PS C:\> Do-Something |
如果调用者也用一个错误处理函数来接收它,则会出现这种情况:
1 | try |
结果看起来如下:
1 | Message Originalmessage |
这样调用者可以看到返回的错误信息,并且经过内部处理之后,还可以传递原始的错误信息。
假设您想以不同的身份打开多个 PowerShell 控制台,或以其他人的身份打开任何程序。
要实现这个目标,您需要以其他人的身份登录,这很明显是个负担。以下是将凭据以安全的方式保存到文件的方法:密码采用您的身份和您的机器加密成密文。只有保存它们的那个人可以取回它,而且只能在保存该文件的机器上操作:
1 | # saving credential securely to file |
这个凭据将保存到用户配置中。如果您希望保存到其它地方,请改变路径。喜欢保存多少份,就调用多少次该方法。
下一步,假设您加载了一个保存的凭据,并且使用该身份启动了一个程序:
1 | # getting back saved credential |
这将以您之前指定的用户身份创建一个新的 PowerShell 实例——无需手动登录。
PowerShell 同时有五个主要版本在发行。除掉最新的小版本,例如 Windows 10 和 Server 2016 上的 PowerShell 5.1。加上 beta 版和预发行版本,以及 Linux 和 Nano 服务器上的 PowerShell。哇哦!
要跟踪这些版本,并知道正在用哪个版本、它从哪里来、可能有哪些兼容性问题很不容易。MVP 大学里的 Egil Ring 维护着一个很酷的模块。如果您装了 PowerShell 5 或从 powershellgallery.com 安装了 PowerShellGet,您可以用一行代码下载这个模块:
1 | PS C:\> Install-Module -Name PSVersion -Scope CurrentUser |
回答几个问题后,该模块安装完成。它只包含两个命令:
1 | PS C:\> Get-Command -Module PSVersion |
PSVersion 是一个社区项目,跟踪 PowerShell 的发行编号、它们的含义、它们的来源:
1 | PS C:\> Get-PSVersion -ListVersion |
这是在您的企业中以友好的 PowerShell 版本名称获得 PowerShell 版本号的方法:
1 | PS C:\> Get-PSVersion |
使用 Update-Help
下载 PowerShell 的帮助文件时,PowerShell 5 有一个 bug,目前可以修复:基于文本的帮助文件扩展名是“.txt”而不是“.help.txt”,所以 PowerShell 帮助系统会忽略它们。您可以自己试验一下——以下命令可能会返回一大堆关于主题:
1 | PS C:\> Get-Help about* |
如果没有显示上述的内容,您也许还没有事先运行过 Update-Help
来下载帮助文件,或者您被 bug 吃了。
无论这个 bug 是否正在修复中,用 PowerShell 您可以轻松地修改这些东西。以下是一个用于修复这些受影响的帮助文件的扩展名的脚本。
这个脚本需要管理员权限,因为帮助文件是位于受保护的 Windows 文件夹中:
1 | # find all text files inside the PowerShell folder that start |
当您创建 PowerShell 函数时,参数可以是命名的也可以是按位置的。以下是一个例子:
如果您想检测文件系统中的非法字符,以下是一个简单的适配:
1 | function Test-Command |
如您所见,使用按位置的参数(只需要指定参数,不需要显式地指定参数名)可能更适用于为特定目的编写的代码,但是可读性更差。这是有可能的,因为上述函数的语法看起来如下:
1 | Test-Command [[-Name] <string>] [[-Id] <int>] |
那么一个编写一个效果相反的 PowerShell 函数,实现这种语法呢:
1 | Test-Command [-Name <string>] [-Id <int>] [<CommonParameters>] |
目前这个方法比较生僻,不过是完全可行的:
1 | function Test-Command |
一旦开始使用参数集合,缺省情况下所有参数都是命名的参数。
当您创建一个 PowerShell 函数时,所有参数都有默认的位置,除非人为地加上“Position
”属性。一旦加上这个属性,所有不带“Position
”的参数将立刻变为命名的必须参数。让我们看看例子:
这是一个经典的函数定义,创建了三个固定位置的参数:
1 | function Test-Command |
语法如下:
1 | Test-Command [[-Name] <string>] [[-ID] <int>] [[-Email] <string>] |
一旦您在任何一个参数上添加“Position
”属性,其它的就变为命名的参数:
1 | function Test-Command |
以下是它的语法:
1 | Test-Command [[-Name] <string>] [[-ID] <int>] [-Email <string>] [<CommonParameters>] |
区别在哪?您不需要指定参数名 -Name
和 -ID
,但如果您希望为第三个参数指定一个值,必须指定 -Email
。在第一个例子中,所有三个参数都可以按照位置来定位。
有时候 PowerShell 脚本只是用来生成某些东西,比如说生成报告,而不需要用 Excel 或记事本打开它。这时候您不会希望执行 PowerShell 的时候显示 PowerShell 控制台窗口。
并没有很简单的方法能隐藏 PowerShell 的控制台窗口,因为即使用了 -WindowsStyle Hidden
参数,也会先显示控制台,然后隐藏它(一闪而过)。
一种方法是使用 Windows 快捷方式来启动脚本。右键单击桌面的空白区域,然后选择新建/快捷方式。就可以新建一个快捷方式。当提示输入地址的时候,键入这行代码:
1 | powershell -noprofile -executionpolicy bypass -file "c:\path\to\script.ps1" |
点击“下一步”,然后添加脚本的名称,再点击“下一步”,就接近完成了。这个快捷方式显示蓝色的 PowerShell 图标。单击它的时候,脚本即可运行,只是还不是隐藏的。
您现在只需要右键单击新创建的快捷方式,选择“属性”,然后将“运行”的设置从“正常窗口”改为您想要的设置。您也可以设置一个快捷方式,这需要管理员权限。
一个缺点是,在 Windows 10 中,“运行”的设置不再包含隐藏程序的选项。您最多可以最小化执行。
PowerShell 5.1(随 Windows 10 和 Server 2016 发布)带来一系列管理计算机时区的新 cmdlet。Get-TimeZone
返回当前的设置,而 Set-TimeZone
可以改变时区设置:
PS C:\> Get-TimeZone
Id : W. Europe Standard Time
DisplayName : (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm,
Vienna
StandardName : W. Europe Standard Time
DaylightName : W. Europe Daylight Time
BaseUtcOffset : 01:00:00
SupportsDaylightSavingTime : True
在 PowerShell 中,cmdlet 和 function 的唯一根本区别是它们是怎么编程的:函数用的是纯粹的 PowerShell 代码,这也是查看它们的源代码,并学习新东西的有趣之处。
这行代码列出所有当前从 module 中加载的所有 PowerShell function:
1 | Get-Module | ForEach-Object { Get-Command -Module $_.Name -CommandType Function } |
一旦您知道了内存中某个函数的名字,可以用这种方法快捷查看它的源代码。在这些例子中,我们将探索 Format-Hex
函数。只需要将这个名字替换成内存中存在的其它函数名即可:
1 | ${function:Format-Hex} | clip.exe |
这行代码将源代码存入剪贴板,您可以将它粘贴到您喜欢的编辑器中。另外,您也可以用这种方式运行:
1 | Get-Command -Name Format-Hex -CommandType Function | |
如果您需要在 64 位脚本中运行 32 位 PowerShell 代码,假设您是管理员,并且使用远程操作功能,您可以远程操作您的系统:
1 | $code = { |
这将在 32 位环境中运行 $code
中的脚本块。该指针返回的 size 是 4,这是 32 位的证据。当您直接运行脚本块,返回的是 8 字节(64 比特)。