catch块中的Powershell异常会导致意外的执行流程

时间:2015-11-29 16:03:28

标签: powershell error-handling try-catch throw

我想了解为什么以下代码打印出来"我不应该打印!!"

try块中的代码抛出一个错误,因为它的errorAction变量设置为stop,Ppowershell将执行相应的catch块。

在catch测试中,函数调用将失败(因为测试函数param属性validateSet不包含" fasdfsd")并抛出明显的非终止异常:ParameterArgumentValidationError

我现在期望在测试函数调用之后执行恢复,打印"捕获异常"并退出,但它跳出catch块并继续执行打印"我不应该打印!!"。

我错过了什么?

谢谢, 的Davide

function test-function{
[CmdletBinding()]
PARAM(
    [Parameter(Mandatory)]
    [ValidateSet('OnlyAcceptThisStringAndNothingElse')]
    [ValidateNotNullOrEmpty()]
    [string]$param
)
    write-output "Executed"

}

try{
    ## throws an error to enter the catch block
    get-childitem nonexisting -ea stop
}
catch{
    ## test-function does not accept this param and throws an error
    test-function -param "fasdfsd"
    ## this should be executed anyway but it isn't
    write-output "caught exception"
    ## the script should quit here
    exit
}
## this Should not have been executed but it is
write-output "I shouldn't be printed!!"

1 个答案:

答案 0 :(得分:0)

  

@beatcracker 如果一切正常,那么在非。之后   终止异常,执行应继续下一步   写入输出的指令"捕获异常"不久之后   命令"退出"哪个应该退出剧本。

- Davide Talesco

我同意,我对此并不清楚。我的意思是它始终以这种方式工作 - 必须将catch块中的所有内容包装到另一个try / catch中,否则它将在非终止错误时默默地退出catch scriptblock并继续执行下一个代码。

<小时/> 下面不是一个真正的答案,只是一个希望的蠢货,一个更熟练的人可以理解它。 虽然我无法理解为什么会这样做,但我认为我已将其本地化为System.Management.Automation汇编(public class ScriptBlock)中的代码:

internal void InvokeWithPipe(bool useLocalScope, bool writeErrors, object dollarUnder, object input, object scriptThis, Pipe outputPipe, ref ArrayList resultList, params object[] args)
{
    ExecutionContext contextFromTLS = this.GetContextFromTLS();
    if (contextFromTLS.CurrentPipelineStopping)
    {
        throw new PipelineStoppedException();
    }
    ParseTreeNode codeToInvoke = this.GetCodeToInvoke();
    if (codeToInvoke != null)
    {
        InvocationInfo invocationInfo = new InvocationInfo(null, codeToInvoke.NodeToken, contextFromTLS);
        contextFromTLS.Debugger.PushMethodCall(invocationInfo, this);
        bool flag = false;
        ScriptInvocationContext oldScriptContext = null;
        Pipe shellFunctionErrorOutputPipe = null;
        CommandOrigin scopeOrigin = contextFromTLS.EngineSessionState.currentScope.ScopeOrigin;
        Exception exception = null;
        SessionStateInternal engineSessionState = contextFromTLS.EngineSessionState;
        ActivationRecord oldActivationRecord = null;
        try
        {
            ScriptInvocationContext scriptContext = new ScriptInvocationContext(useLocalScope, scriptThis, dollarUnder, input, args);
            this.EnterScope(contextFromTLS, scriptContext, out oldScriptContext, out oldActivationRecord);
            shellFunctionErrorOutputPipe = contextFromTLS.ShellFunctionErrorOutputPipe;
            if (!writeErrors)
            {
                contextFromTLS.ShellFunctionErrorOutputPipe = null;
            }
            contextFromTLS.EngineSessionState.currentScope.ScopeOrigin = CommandOrigin.Internal;
            if (!string.IsNullOrEmpty(this.File))
            {
                contextFromTLS.Debugger.PushRunning(this.File, this, false);
                flag = true;
            }
            codeToInvoke.Execute(null, outputPipe, ref resultList, contextFromTLS);
        }
        catch (ReturnException exception2)
        {
            if (!this._isScriptBlockForExceptionHandler)
            {
                ParseTreeNode.AppendResult(contextFromTLS, exception2.Argument, null, ref resultList);
            }
            else
            {
                exception = exception2;
            }
        }
        finally
        {
            if (flag)
            {
                contextFromTLS.Debugger.PopRunning();
            }
            contextFromTLS.ShellFunctionErrorOutputPipe = shellFunctionErrorOutputPipe;
            contextFromTLS.EngineSessionState.currentScope.ScopeOrigin = scopeOrigin;
            try
            {
                this.LeaveScope(contextFromTLS, oldScriptContext, engineSessionState, oldActivationRecord);
            }
            finally
            {
                contextFromTLS.Debugger.PopMethodCall();
            }
        }
        if (exception != null)
        {
            throw exception;
        }
    }
}

catch的Scriptblocks是通过内部密封的_isScriptBlockForExceptionHandler方法trueExceptionHandlerNode设置为Invoke创建的,该方法从上述方法调用CreateExceptionHandler公共课ScriptBlock

internal static ScriptBlock CreateExceptionHandler(ParseTreeNode body, Token token, int pipelineSlots, int variableSlots)
{
    return new ScriptBlock(token, null, null, null, null, body, null, false, null, null, null, pipelineSlots, variableSlots) { _isScriptBlockForExceptionHandler = true };
}

请注意,当_isScriptBlockForExceptionHandler设置为true时,如果发生异常,则在上面的InvokeWithPipe方法中执行catch块时不会抛出异常:

catch (ReturnException exception2)
{
    if (!this._isScriptBlockForExceptionHandler)
    {
        ParseTreeNode.AppendResult(contextFromTLS, exception2.Argument, null, ref resultList);
    }
    else
    {
        exception = exception2;
    }
}

AppendResult方法的作用对我来说并不清楚,但我发现this

  

调用AppendResult方法基本上调用了getter   返回的对象的公共属性检索值   将被写入输出控制台。