Git clone:将stderr重定向到stdout但是将错误写入stderr

时间:2016-01-15 22:31:31

标签: git powershell git-clone

git clone将其输出写入stderr,记录为here。我可以使用以下命令重定向它:

git clone https://myrepo c:\repo 2>&1

但是,这会将所有输出(包括错误)从stderr重定向到stdout。有没有办法将进度消息重定向到stdout,但仍有错误消息写入stderr

7 个答案:

答案 0 :(得分:7)

MingW更新提供了一种使用Git 2.15.x / 2.16(2018年第一季度)处理重定向的新方法

commit b2f5571点击commit 1a172e4commit 3f94442Johannes Schindelin (dscho)(2017年11月1日)。{ Junio C Hamano -- gitster --于2017年11月9日commit 421f21c合并)

  

mingw:添加实验性功能以重定向标准句柄

     

特别是在从应用程序(如Visual Studio的团队资源管理器)调用Git时,正确关闭stdin / stdout / stderr非常重要。
  但是,当在Windows上生成进程时,如果我们想要使用它们,那些句柄必须被标记为可继承,但是该标志是一个全局标志,并且很可能被其他生成的进程使用,然后这些进程不知道要关闭这些句柄。 / p>      

让我们介绍一组环境变量(GIT_REDIRECT_STDIN和朋友),它们指定文件的路径,甚至更好的命名管道(类似于Unix套接字),并由生成的Git进程使用。登记/>   这有助于解决上述问题:这些命名管道将在启动时以不可继承的方式打开,并且不会传递任何句柄(因此任何生成的子项都不需要关闭继承的句柄)。

     

自v2.11.0(2)以来,Git for Windows附带此功能(标记为实验性),因此在此期间已经看到了一些严重的测试。

Git documentation现在包括:

GIT_REDIRECT_STDIN:
GIT_REDIRECT_STDOUT:
GIT_REDIRECT_STDERR:
  

仅限Windows:允许将标准输入/输出/错误句柄重定向到环境变量指定的路径。   这在多线程应用程序中特别有用,在这些应用程序中,通过CreateProcess()传递标准句柄的规范方法不是一种选择,因为它需要将句柄标记为可继承(因此每个生成的进程会继承它们,可能会阻止常规的Git操作)。

     

主要用途是使用命名管道进行通信(例如\\.\pipe\my-git-stdin-123)。

它补充道:

  

mingw:可选择通过相同的句柄

重定向stderr / stdout      

Powershell和Unix shell中的“2>&1”符号表示stderr   被重定向到已写入stdout的同一句柄。

     

让我们使用这个特殊值来允许与GIT_REDIRECT_STDERRGIT_REDIRECT_STDOUT相同的技巧:如果前者的值是   2>&1,然后将stderr简单地写入与stdout相同的句柄。

     

Jeff Hostetler建议使用该功能。

答案 1 :(得分:5)

我使用这个脚本来运行git命令。由于git即使成功也会写入stderr(例如同步时拉取),这会处理这些情况并写出输出的第一行,这通常是你需要知道的。

<#
.Synopsis
    Invoke git, handling its quirky stderr that isn't error

.Outputs
    Git messages, and lastly the exit code

.Example
    Invoke-Git push

.Example
    Invoke-Git "add ."
#>
function Invoke-Git
{
param(
[Parameter(Mandatory)]
[string] $Command )

    try {

        $exit = 0
        $path = [System.IO.Path]::GetTempFileName()

        Invoke-Expression "git $Command 2> $path"
        $exit = $LASTEXITCODE
        if ( $exit -gt 0 )
        {
            Write-Error (Get-Content $path).ToString()
        }
        else
        {
            Get-Content $path | Select-Object -First 1
        }
        $exit
    }
    catch
    {
        Write-Host "Error: $_`n$($_.ScriptStackTrace)"
    }
    finally
    {
        if ( Test-Path $path )
        {
            Remove-Item $path
        }
    }
}

答案 2 :(得分:3)

你可以摆脱stderr

通过此命令:

git clone https://myrepo c:\repo 2>$null

通过这样做,所有stderr都不会显示。

您无法显示进度并仅丢弃错误。如果命令失败,如果成功stderr

,则所有输出都为stdout

编辑: 看起来git命令输出总是stderr,即使该命令仅在Windows上成功。 吨。

答案 3 :(得分:2)

我已经修改了Invoke-Git以更好地满足我的需求。

从我在寻找解决方案时阅读的许多帖子中,我猜有很多人可以使用它。

享受。

该版本将:

  • 执行传入的Git命令(假设Git已在执行路径中。)
  • 如果一切顺利,则所有输出(stdout和stderr)都将显示在主机上,而不是通过stderr。
  • 检查$ LASTEXITCODE以查看是否确实存在错误。如果出现错误,则所有输出都将被抛出给调用者以进行处理。
<#
.Synopsis
    Invoke git, handling its quirky stderr that isn't error

.Outputs
    Git messages

.Example
    Invoke-Git push

.Example
    Invoke-Git "add ."
#>
function Invoke-Git
{
param(
[Parameter(Mandatory)]
[string] $Command )

    try {
        # I could do this in the main script just once, but then the caller would have to know to do that 
        # in every script where they use this function.
        $old_env = $env:GIT_REDIRECT_STDERR
        $env:GIT_REDIRECT_STDERR = '2>&1'

        Write-Host -ForegroundColor Green "`nExecuting: git $Command "
        $output = Invoke-Expression "git $Command "
        if ( $LASTEXITCODE -gt 0 )
        {
            # note: No catch below (only the try/finally). Let the caller handle the exception.
            Throw "Error Encountered executing: 'git $Command '"
        }
        else
        {
            # because $output probably has miultiple lines (array of strings), by piping it to write-host we get multiple lines.
            $output | Write-Host 
        }
    }
    # note: No catch here. Let the caller handle it.
    finally
    {
        $env:GIT_REDIRECT_STDERR = $old_env
    }
}

答案 4 :(得分:2)

一般来说

  • 控制台(终端)应用程序-无论是在Windows还是在类似Unix的平台上-只能使用两个输出流:

    • 标准输出(标准输出)-这就是 数据 (“返回值”)的位置。
    • stderr (标准错误)-这是其中的错误消息,并且-由于缺少其他流-不是数据的其他任何内容< / em>,例如进度和状态信息。
  • 因此,不能也不应该根据stderr输出的存在来推断成功与失败

    • 相反,您必须仅依靠应用程序的进程退出代码

      • 0 表示 成功
      • 任何 非零表示 失败
    • PowerShell 中,**进程退出代码反映在自动变量$LASTEXITCODE**中。


具体 ,这意味着:

  • 鉴于git的stderr输出行,您无法推断它们是否代表 error 消息或其他类型的非数据信息,例如作为进度或状态消息,git经常使用。

    • 告诉git将其stderr输出分类重定向到stdout(通过在PowerShell中将环境变量GIT_REDIRECT_STDERR设置为字符串2>&1$env:GIT_REDIRECT_STDERR = '2>&1'不是 help,因为错误消息和进度/状态消息都将发送到那里。
  • 如前所述,您只能从非零退出代码推断失败。


一种务实的方法是要执行以下操作:

# Invoke git and capture both its stdout and stderr streams.
$result = git clone https://myrepo c:\repo 2>&1
# Throw an error, if git indicated failure.
if ($LASTEXITCODE) { Throw "git failed (exit code: $LASTEXITCODE):`n$($result -join "`n")" }                                            #`
# Output the captured result, as an array of strings
$result | % ToString 

注意:

  • | % ToString确保仅输出 strings ,因为成功将stderr行(通过流2重定向到(>)输出流(&1)包装在System.Management.Automation.ErrorRecord实例中。

  • 2>&1可能会有意外的副作用-有关背景信息,请参见this answer

  • this RFC draft的主题是将外部程序调用更好地集成到PowerShell的错误处理中,尤其是当遇到非零退出代码时,可以选择自动中止执行。

答案 5 :(得分:2)

我只是想补充一下,如果像我一样,您更关心在stderr中出现错误和仅错误,并且不关心任何一种进展,那么有一个非常简单的解决方法-您可以在命令中添加--quiet(或-q)。

这告诉git完全停止报告进度,除非出现 actual 错误。

答案 6 :(得分:0)

这是另一种可能成为某些人灵感的方法。正如其他人指出的那样,重定向和检查退出代码效果很好。与其他答案不同的地方:

  • 重定向到文件有点烦人,因为只要git正在运行,您什么都不会发生,因此只需返回输出并使用单独的错误消息即可。这是一个折衷,但我更喜欢。
  • 使用Write-Verbose而不是Write-Host,因为前者是可配置的
  • 使用Write-Error而不是苛刻的Throw产生错误。使诸如ErrorAction,ErrorVariable之类的功能按预期工作。
  • 支持设置运行目录,就像您使用-C而不是cd进入目录一样,但也支持通常不具有该功能的命令。因此,您可以执行igit -dir some/path stash。我主要在自动脚本中使用它,否则不得不将CD插入目录会很烦人。
  • 使用ValueFromRemainingArguments等,以便可以像直接编写git命令一样传递命令,因此不需要字符串,但仍然允许它。因此igit checkout master的工作方式类似于igit 'checkout master'。几乎就是因为标准PS警告适用:引用,因此如果需要将引号传递给基础命令,则仍需要一个实际的字符串,即igit log '-format =“%h%d”'{{ 1}} igit push -v . And PS doesn't require you to type full parameter names meaning igit push -Verbose will be interpreted as-v instead of passing igit-强制-v`或写引号。

代码:

i.e. verbose push to git. Use double dash to deal with that