PowerShell - 将可执行文件的stderr重定向到文件或变量,但仍然有stdout转到控制台

dea*_*dog 8 powershell redirect stream stderr powershell-3.0

我正在写一个脚本从GitHub下载几个存储库.以下是下载存储库的命令:

git clone "$RepositoryUrl" "$localRepoDirectory"
Run Code Online (Sandbox Code Playgroud)

当我运行此命令时,它会在我想要显示的控制台窗口中显示一些很好的进度信息.

问题是我还希望能够检测下载时是否发生任何错误.我发现这篇文章谈到重定向各种流,所以我试过:

(git clone "$RepositoryUrl" "$localRepoDirectory") 2> $errorLogFilePath
Run Code Online (Sandbox Code Playgroud)

这会将stderr中的任何错误传递给我的文件,但不再在控制台中显示好的进度信息.

我可以这样使用Tee-Object:

(git clone "$RepositoryUrl" "$localRepoDirectory") | Tee-Object -FilePath $errorLogFilePath
Run Code Online (Sandbox Code Playgroud)

我仍然得到了很好的进度输出,但是这会将stdout传递给文件,而不是stderr; 我只关心检测错误.

有没有办法可以存储文件或(最好)变量中发生的任何错误,同时还有进度信息传递到控制台窗口?我有一种感觉,答案可能在于将各种流重定向到其他流中,如本文中的讨论,但我不太确定.

========更新=======

我不确定git.exe是否与典型的可执行文件不同,但我已经做了一些测试,这是我发现的:

$output = (git clone "$RepositoryUrl" "$localRepoDirectory")
Run Code Online (Sandbox Code Playgroud)

$ output始终包含文本"Cloning into'[localRepoDirectory]'...",无论命令是成功完成还是产生错误.此外,执行此操作时,仍会将进度信息写入控制台.这让我认为进度信息不是通过stdout写的,而是通过其他一些流写的?

如果发生错误,则错误将写入控制台,但采用通常的白色前景色,不是典型的红色表示错误,黄色表示警告.当从cmdlet函数中调用此函数并且命令因错误而失败时,错误不会通过函数的-ErrorVariable(或-WarningVariable)参数返回(但是,如果我执行自己的Write-Error,则会通过-ErrorVariable返回).这让我觉得git.exe没有写入stderr,但是当我们这样做时:

(git clone "$RepositoryUrl" "$localRepoDirectory") 2> $errorLogFilePath
Run Code Online (Sandbox Code Playgroud)

错误消息被写入文件,这使我认为它写入stderr.所以现在我很困惑......

========更新2 =======

因此,在Byron的帮助下,我尝试了一些使用新流程的解决方案,但仍然无法获得我想要的东西.使用新进程时,我永远无法将好的进度写入控制台.

我尝试过的三种新方法都使用了这个共同的代码:

$process = New-Object System.Diagnostics.Process
$process.StartInfo.Arguments = "clone ""$RepositoryUrl"" ""$localRepoDirectory"""
$process.StartInfo.UseShellExecute = $false
$process.StartInfo.RedirectStandardOutput = $true
$process.StartInfo.RedirectStandardError = $true
$process.StartInfo.CreateNoWindow = $true
$process.StartInfo.WorkingDirectory = $WORKING_DIRECTORY
$process.StartInfo.FileName = "git"
Run Code Online (Sandbox Code Playgroud)

方法1 - 在新进程中运行并在之后读取输出:

$process.Start()
$process.WaitForExit()
Write-Host Output - $process.StandardOutput.ReadToEnd()    
Write-Host Errors - $process.StandardError.ReadToEnd()
Run Code Online (Sandbox Code Playgroud)

方法2 - 同步获取输出:

$process.Start()
while (!$process.HasExited)
{
    Write-Host Output - $process.StandardOutput.ReadToEnd()
    Write-Host Error Output - $process.StandardError.ReadToEnd()

    Start-Sleep -Seconds 1
}
Run Code Online (Sandbox Code Playgroud)

即使看起来它会在进程运行时写入输出,但在进程退出之前它不会写任何内容.

方法3 - 异步获取输出:

Register-ObjectEvent -InputObject $process -EventName "OutputDataReceived" -Action {Write-Host Output Data - $args[1].Data }
Register-ObjectEvent -InputObject $process -EventName "ErrorDataReceived" -Action { Write-Host Error Data - $args[1].Data }
$process.Start()
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
while (!$process.HasExited)
{
    Start-Sleep -Seconds 1
}
Run Code Online (Sandbox Code Playgroud)

这会在进程正常工作时输出数据,这很好,但它仍然不会显示很好的进度信息:(

Kei*_*ill 1

您可以通过将 git clone 命令放入高级函数中来完成此操作,例如:

function Clone-Git {
    [CmdletBinding()]
    param($repoUrl, $localRepoDir)

    git clone $repoUrl $localRepoDir
}

Clone-Git $RepositoryUrl $localRepoDirectory -ev cloneErrors

$cloneErrors
Run Code Online (Sandbox Code Playgroud)

  • 我对这个答案感到非常兴奋,因为它看起来很简单。不幸的是,它似乎不适用于我的情况。git.exe 似乎不使用 stderr,因为 $cloneErrors 变量始终为 null。但是,它确实将错误消息写入控制台,但采用常规的白色前景色,而不是典型的红色表示错误或黄色表示警告。但是,使用“2>”确实会将错误消息通过管道传输到文件,因此看起来像是在使用 stderr。我很困惑。如果我从 Clone-Git 函数执行自己的 Write-Error,$cloneErrors 变量会显示我的错误。还有其他提示吗? (2认同)