所以我要做的是重新创建一个类似于我在Bash中使用的函数,但在Powershell中:
yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "FAILED: $*"; }
目前我最感兴趣的部分是try((函数。本质上,它所做的是允许我用这个函数包装一个命令,并让它管理退出代码。效果是这样的:
try doSomething -args
如果doSomething
以非零退出,它将向stderr输出命令并停止脚本执行。
我意识到Powershell有一个错误操作,可以用来中止脚本,但它似乎只适用于commandlets。我需要一些可以在整个脚本中的任何内容上使用的东西。我还希望避免大量冗长的try/catch逻辑使脚本变得粗糙,因此需要像try/yell/die这样优雅的东西。通过这种方式,我可以单独在这个函数中编写处理,并使用它来调用任何我想要处理的东西。
我找到了$MyInvocation
,并认为这可能是进入的方法,但我似乎找不到从函数中实际执行它的方法。例如:
function run() {
$MyInvocation # ?? what do??
}
run doSomething -args
我想我可以自己解决剩下的问题,我只是不太知道如何编写这个包装器函数。有什么想法吗?
更新
所以我做了一些俗气的事情,我删除了命令,对剩下的东西做了一个Invoke-Expression
,它似乎起了作用。它感觉超级黑客,所以我仍然对想法持开放态度:
function attempt() {
$thisCommand = $MyInvocation.Line.Trim()
Write-Output $thisCommand
Invoke-Expression $thisCommand.Substring(8)
if($LASTEXITCODE -ne 0) {
throw "Command failed $thisCommand"
exit 111
}
}
attempt doSomething -args
如果您希望函数运行任意命令,并在该命令失败时抛出错误,您可以执行以下操作:
function Test-Command {
try {
$cmd, $params = $args
$params = @($params)
$global:LastExitCode = 0
$output = & $cmd @params 2>&1
if ($global:LastExitCode -ne 0) {
throw $output
}
$output
} catch {
throw $_
}
}
细分:
$cmd, $params = $args
取自动变量$args
(传递给函数的参数数组(,并将其第一个元素分配给变量$cmd
,其余元素分配给参数$params
。
$params = @($params)
确保$params
包含一个数组(下一步需要(,即使变量为空或仅包含一个值。
CCD_ 10使用调用运算符CCD_。不要使用Invoke-Expression
。
重定向运算符2>&1
将错误输出与正常输出合并,以便在变量$output
中捕获两个输出流。
如果$cmd
是PowerShell cmdlet,则错误将引发异常,该异常由try
语句捕获。然后将跳过try
块中的其余代码。但是,请注意,并非PowerShell cmdlet引发的所有错误都是自动终止的错误(例如,请参阅Scripting Guy博客上的"PowerShell错误处理简介">(。要将非终止错误转换为终止错误,您需要设置$ErrorActionPreference = 'Stop'
(并在完成后将其重置为原始值(。
如果$cmd
是外部命令,错误不会引发异常,但自动变量$LastExitCode
会用命令的退出代码更新。返回非零退出代码的命令将触发if
条件,并使用命令输出作为异常消息引发自定义异常。该异常随后也被try
语句捕获。再次跳过try
块中的其余代码。
CCD_ 24在每次运行之前重置变量CCD_。这是必要的,因为只有外部命令返回退出代码,而PowerShell cmdlet则不返回。由于$LastExitCode
保留了上次在当前会话中运行的外部命令的退出代码,因此不重置变量将混淆在外部命令之后运行的PowerShell cmdlet的状态检测。
只有当命令既不抛出异常也不返回非零退出代码时,才能到达try
块中的最后一行,该行将返回捕获的命令输出。
任何捕获到的异常都在catch
块中处理,该块只是将异常传递给函数的调用方。当然,您也可以输出错误并退出,而不是抛出。