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

时间:2018-12-18 17:05:56

标签: linux powershell-core powershell-v6.0

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

yell() { echo "$0: $*" >&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

1 个答案:

答案 0 :(得分:1)

如果您希望函数运行任意命令并在该命令失败时引发错误,则可以执行以下操作:

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 &调用命令,而splatting使用参数。 Do NOT use Invoke-Expression

redirection operator 2>&1将错误输出与常规输出合并,以便将两个输出流都捕获到变量$output中。

如果$cmd PowerShell cmdlet ,则错误将引发异常,该异常将由try语句捕获。 try块中的其余代码将被跳过。但是请注意,并非所有PowerShell cmdlet引发的错误都会自动终止错误(例如,参见Scripting Guy博客上的"An Introduction to Error Handling in PowerShell")。要将非终止错误转换为终止错误,您需要设置$ErrorActionPreference = 'Stop'(完成后将其重置为原始值)。

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

$global:LastExitCode = 0在每次运行之前重置变量$LastExitCode。这是必需的,因为只有外部命令会返回退出代码,而PowerShell cmdlet不会。由于$LastExitCode保留了当前会话中上次运行的外部命令的退出代码,因此不重置变量将使在执行外部命令后运行的PowerShell cmdlet的状态检测变得混乱。

仅在命令既不引发异常也不返回非零退出代码的情况下,才到达try块的最后一行,该行回显捕获的命令输出。

任何捕获到的异常都在catch块中进行处理,该块只是将异常传递给函数的调用者。当然,除了抛出错误外,您还可以输出错误并退出。