PowerShell:使用Invoke-Expression管理错误

时间:2014-02-05 17:10:50

标签: powershell

我试图弄清楚如何确定使用Invoke-Expression的命令是否失败。 即使变量$?,$ LASTEXITCODE或-ErrorVariable也无法帮助我。

例如:

PS C:\> $cmd="cat c:\xxx.txt"

使用Invoke-Expression

调用$ cmd

PS C:\> Invoke-Expression $cmd -ErrorVariable err

Get-Content : Cannot find path 'C:\xxx.txt' because it does not exist. At line:1 char:4 + cat <<<< c:\xxx.txt + CategoryInfo : ObjectNotFound: (C:\xxx.txt:String) [Get-Content], ItemNotFoundExcep tion + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

$?是真的

PS C:\> $?

True

$ LASTEXITCODE为0

PS C:\> $LASTEXITCODE

0

$ err是空的

PS C:\> $err

PS C:\>

我找到的唯一方法是在文件中重定向STD_ERR并测试此文件是否为空

PS C:\> Invoke-Expression $cmd 2>err.txt

PS C:\> cat err.txt

Get-Content:找不到路径'C:\ xxx.txt',因为它不存在。 在行:1 char:4 + cat&lt;&lt;&lt;&lt; C:\ xxx.txt     + CategoryInfo:ObjectNotFound:(C:\ xxx.txt:String)[Get-Content],ItemNotFoundExcep    重刑     + FullyQualifiedErrorId:PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

这是唯一且最好的方法吗?

2 个答案:

答案 0 :(得分:3)

我疯狂地尝试将STDERR流捕获到变量工作中。我终于解决了。 invoke-expression命令中存在一个怪癖,它使整个2&amp;&gt; 1重定向失败,但是如果省略1则它会做正确的事。

 function runDOScmd($cmd, $cmdargs)
 {
 # record the current ErrorActionPreference
     $ep_restore = $ErrorActionPreference
 # set the ErrorActionPreference  
     $ErrorActionPreference="SilentlyContinue"

 # initialize the output vars
     $errout = $stdout = ""

 # After hours of tweak and run I stumbled on this solution
 $null = iex "& $cmd $cmdargs 2>''"  -ErrorVariable errout -OutVariable stdout
 <#                       these are two apostrophes after the >
     From what I can tell, in order to catch the stderr stream you need to try to redirect it,
     the -ErrorVariable param won't get anything unless you do. It seems that powershell
     intercepts the redirected stream, but it must be redirected first.
 #>
 # restore the ErrorActionPreference
 $ErrorActionPreference=$ep_restore

 # I do this because I am only interested in the message portion
 # $errout is actually a full ErrorRecord object
     $errrpt = ""
     if($errout)
     {
         $errrpt = $errout[0].Exception
     }

 # return a 3 member arraylist with the results.
     $LASTEXITCODE, $stdout, $errrpt
 }

答案 1 :(得分:1)

听起来你在尝试捕获变量中的native的错误输出而不捕获stdout。如果捕获标准输出是可以接受的,则使用2&gt;&amp; 1。

重定向到文件可能是最简单的。使用Invoke-Expression为它的-ErrorVariable参数几乎看起来是个好主意,但是Invoke-Expression有很多问题,我通常不鼓励它。

另一种选择看起来有点麻烦,但它可以作为一种功能考虑在内。该想法是使用2&gt;&amp; 1合并输出流,但是然后基于对象的类型再次拆分它们。它可能看起来像这样:

function Split-Streams
{
    param([Parameter(ValueFromPipeline=$true)]$InputObject)

    begin
    {
        $stdOut = @()
        $stdErr = @()
    }

    process
    {
        if ($InputObject -is [System.Management.Automation.ErrorRecord])
        {
            # This works well with native commands but maybe not as well
            # for other commands that might write non-strings
            $stdErr += $InputObject.TargetObject
        }
        else
        {
            $stdOut += $InputObject
        }
    }

    end
    {
        ,$stdOut
        ,$stdErr
    }
}

$o, $e = cat.exe c:\xxx.txt 2>&1 | Split-Streams