powershell 如何判断ScriptBlock是否成功?

atmip9wb  于 2023-02-19  发布在  Shell
关注(0)|答案(1)|浏览(125)

我想知道ScriptBlock是否成功执行。
如果我跑了

1/0; echo $?

我得到

RuntimeException: Attempted to divide by zero.
false

但如果我做了

$s = { 1/0 }; Invoke-Command $s; echo $?

我得到

RuntimeException: Attempted to divide by zero.
true

我假设$?指的是Invoke-Command的执行,但是如何才能使脚本块本身成功呢?
我不需要使用Invoke-Command,如果改为$s.Invoke()是可行的,那么愉快地使用它。

    • 背景**

我想为DSL编写一个函数,类似于

function at_place {
    Param(
        [string] $Path,
        [scriptblock] $ScriptBlock
    )

    Push-Location $Path ;

    Invoke-Command $ScriptBlock ;
    # following line doesn't work        
    [bool] $ScriptBlockPass = $? ;
    
    If ( $? ){
        Write-Debug "success!" ;
        Pop-Location ;
    } Else {
        Write-Error "ScriptBlock failed, remaining at $Path, please fix manually." ;
        throw "ScriptBlock failed at $Path" ;
    }
}

我的缓冲区里有

Push-Location ~/foo; doStuff; If ( $? ){ Pop-Location; } Else { Write-Error "Failed, fix here" }

我想把它写成

at_place ~/foo { doStuff; }

我的 * 实际 * 更改是关于使用Git忽略一堆文件,隐藏它们,应用一些编辑,然后重新忽略它们等。在这里简化,希望能更广泛地适用,减少分心。

chy5wohz

chy5wohz1#

你的问题有点宽泛,答案是,这要看情况。
子作用域调用只能使用.WriteError方法或.ThrowTerminatingError方法通过$PSCmdlet更新$?的值,如关于自动变量文档中所述。这意味着脚本块或函数是advanced one

$s = {
    [CmdletBinding()]
    param()

    try {
        1 / 0
    }
    catch {
        $PSCmdlet.WriteError($_)
    }
}

& $s # Writes Error
$?   # Value will be False (Failure)

对于非高级函数或脚本块,$?将被归类为不可靠,了解脚本块是否成功的一种方法(成功是指没有错误),在我看来,最可靠的方法是将错误首选项设置为Stop,以同时处理这两个错误,终止非终止错误并在try / catch / finally中执行脚本块:

$s = { Write-Error foo }

try {
    $previousPreference = $ErrorActionPreference
    $ErrorActionPreference = 'Stop'
    & $s
}
catch {
    'had errors'
}
finally {
    $ErrorActionPreference = $previousPreference
}

至于新的编辑,我会这样处理您的函数:

function at_place {
    [CmdletBinding()]
    Param(
        [string] $Path,
        [scriptblock] $ScriptBlock
    )

    try {
        $sbSuccess = $true
        $ErrorActionPreference = 'Stop'
        $pushSuccess = Push-Location $Path -PassThru
        & $ScriptBlock
    }
    catch {
        $sbSuccess = $false
        $PSCmdlet.ThrowTerminatingError($_)
    }
    finally {
        if($pushSuccess -and $sbSuccess) {
            Pop-Location
        }
    }
}

相关问题