如何创建运行命令的函数?

How do I create function that runs a command?

所以我想做的是重新创建一个与我在 Bash 中使用的类似的函数,但在 Powershell 中:

yell() { echo "[=10=]: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "FAILED: $*"; }

目前我最感兴趣的部分是这里的 try() 函数。本质上,它的作用是让我用这个函数包装一个命令,并让它管理退出代码。效果是这样的:

try doSomething -args

如果doSomething以非零值退出,它会将命令输出到stderr并停止脚本执行。

我知道 Powershell 有一个错误操作可以用来中止脚本,但它似乎只适用于 commandlet。我需要一些我可以在整个脚本中使用的东西。我还希望避免大量冗长的 try/catch 逻辑使脚本变得混乱,因此需要像 try/yell/die 这样优雅的东西。这样,我就可以单独在这个函数中编写处理,然后用它来调用任何我想处理的东西。

我发现 $MyInvocation 并认为这可能是进入的方式,但我似乎无法找到从函数内部实际执行它的方法。例如:

function run() {
    $MyInvocation # ?? what do??
}

run doSomething -args

我想我可以自己解决剩下的问题,我只是不太清楚如何编写这个包装函数。有什么想法吗?

更新

所以我做了一些俗气的事情,我对命令进行了子字符串处理,并对剩下的内容进行了 Invoke-Expression,这似乎有效。感觉超级hacky,所以我仍然对想法持开放态度:

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 获取 automatic variable $args(传递给函数的参数数组)并将其第一个元素分配给变量 $cmd,其余元素分配给变量 $params.

$params = @($params) 确保 $params 包含一个数组(下一步需要),即使变量为空或仅包含一个值。

& $cmd @params 使用 call operator & while splatting the parameters. Do NOT use Invoke-Expression.

调用命令

redirection operator 2>&1 将错误输出与正常输出合并,因此两个输出流都被捕获在变量 $output.

如果 $cmd 是一个 PowerShell cmdlet 错误将引发异常,该异常会被脚本专家博客上的 try statement. The rest of the code in the try block will then be skipped. Note, however, that not all errors thrown by PowerShell cmdlets are automatically terminating errors (see for instance "An Introduction to Error Handling in PowerShell" 捕获)。要将非终止错误转换为终止错误,您需要设置 $ErrorActionPreference = 'Stop'(并在完成后将其重置为原始值)。

如果$cmd是一个外部命令错误将不会抛出异常,但是自动变量$LastExitCode会更新为命令的退出代码。命令 returning 非零退出代码将触发 if 条件并导致抛出自定义异常,使用命令输出作为异常消息。然后 try 语句也会捕获该异常。 try 块中的其余代码再次被跳过。

$global:LastExitCode = 0 在每个 运行 之前重置变量 $LastExitCode。这是必要的,因为只有外部命令 return 退出代码,而 PowerShell cmdlet 则不需要。由于 $LastExitCode 在当前会话中保留了最后 运行 外部命令的退出代码,因此不重置变量会在外部命令后混淆 PowerShell cmdlet 运行 的状态检测。

try 块中的最后一行,它回显捕获的命令输出,仅当命令既不抛出异常也不 returns 非零退出代码时才会到达。

任何捕获的异常都在 catch 块中处理,它只是将异常传递给函数的调用者。当然,您也可以输出错误并退出,而不是抛出。