bash git stderr 输出无法通过管道传输
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4062862/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
git stderr output can't pipe
提问by Delan Azabani
I'm writing a graphical URI handler for git:// links with bash and zenity, and I'm using a zenity 'text-info' dialog to show git's clone output while it's running, using FIFO piping. The script is about 90 lines long, so I won't bother posting it here, but here's the most important lines:
我正在为带有 bash 和 zenity 的 git:// 链接编写图形 URI 处理程序,并且我正在使用 zenity 'text-info' 对话框来显示 git 在运行时的克隆输出,使用 FIFO 管道。该脚本大约有 90 行长,所以我不会费心在这里发布它,但这里是最重要的几行:
git clone "" "$target" 2>&1 | cat >> /tmp/githandler-fifo &
cat /tmp/githandler-fifo | zenity --text-info --text='Cloning git repository' &
I'm using FIFO instead of a direct pipe to allow them to run asynchronously and allow for killing git if the zenity window is closed.
我使用 FIFO 而不是直接管道,以允许它们异步运行并允许在 zenity 窗口关闭时杀死 git。
Problem is, the only line that appears from git's output is the first:
问题是,git 输出中出现的唯一一行是第一行:
Initialized empty Git repository in /home/delan/a/.git/
The other lines with counting objects, etc. don't show or are shown on the terminal.
带有计数对象等的其他行不显示或显示在终端上。
Current reason
当前原因
The current consensus as to why this isn't working seems to be that catis non-blocking and quits after the first line, only passing that to zenity and not the rest. My aim is to force blocking for the read, and have zenity's text info dialog show all output progressively.
关于为什么这不起作用的当前共识似乎cat是非阻塞并在第一行之后退出,仅将其传递给 zenity 而不是其余的。我的目标是强制阻止读取,并让 zenity 的文本信息对话框逐步显示所有输出。
gitoutputs progress messages (anything other than the "Initialized" message) on stderr, but the moment I try to pipe stderr to a file or to merge with stdout, the messages disappear.
git在 stderr 上输出进度消息(除“已初始化”消息之外的任何消息),但是当我尝试将 stderr 通过管道传输到文件或与 stdout 合并时,消息消失了。
Fix attempt 1
修复尝试 1
I've tried to write two blocking versions of cat's functions in C, bread and bwrite, like this:
我尝试用 C 语言编写 cat 函数的两个阻塞版本,bread 和 bwrite,如下所示:
#include <stdio.h>
main(int argc, char **argv) {
int c;
for (;;) {
freopen(argv[1], "r", stdin);
while ((c = getchar()) != EOF)
putchar(c);
}
}
#include <stdio.h>
main(int argc, char **argv) {
int c;
for (;;) {
freopen(argv[1], "w", stdout);
while ((c = getchar()) != EOF)
putchar(c), fputs("writing", stderr);
}
}
They work nicely because they block and don't quit on EOF, but it hasn't quite solved the problem yet. At the moment, using one, the other, or both, works in theory, but in practice, zenity shows nothing at all now.
它们工作得很好,因为它们会阻塞并且不会在 EOF 上退出,但是还没有完全解决问题。目前,使用一种、另一种或两者在理论上是有效的,但在实践中,zenity 现在完全没有表现出来。
Fix attempt 2
修复尝试 2
@mvds suggested that using a regular file, in combination with tail -frather than cat, may do this. Surprised at such a simple solution (thanks!) I tried it but unfortunately, only the first line showed up in zenity and nothing else.
@mvds 建议使用常规文件,结合tail -f而不是cat,可以做到这一点。对如此简单的解决方案感到惊讶(谢谢!)我试过了,但不幸的是,只有第一行出现在 zenity 中,没有别的。
Fix attempt 3
修复尝试 3
After doing some strace'ing and inspecting git's source code, I realise that git outputs all its progress information (anything past the "Initialized" message) on stderr, and the fact that this is the first line and my assumption that it's because of cat quitting early on EOF was a coincidence/misguided assumption (git doesn't EOF until the program ends).
在做了一些跟踪和检查 git 的源代码之后,我意识到 git 在 stderr 上输出它的所有进度信息(任何超出“初始化”消息的内容),事实上这是第一行,我假设这是因为 cat在 EOF 早期退出是一个巧合/误导的假设(git 在程序结束之前不会 EOF)。
The situation seemed to become a lot simpler, as I shouldn't have to change anything from the original code (at the start of the question) and it should work. Mysteriously, however, the stderr output 'vanishes' when redirected - and this is only something that happens in git.
情况似乎变得简单得多,因为我不应该对原始代码(在问题的开头)进行任何更改,并且它应该可以工作。然而,奇怪的是,重定向时 stderr 输出“消失”了——这只是在 git 中发生的事情。
Test case? Try this, and see if you see anything in the file (you won't):
测试用例?试试这个,看看你是否在文件中看到任何东西(你不会):
git clone git://anongit.freedesktop.org/xorg/proto/dri2proto 2> hurr
This goes against everything I know about stderr and redirection; I've even written a little C program that outputs on stderr and stdout to prove to myself that redirection just doesn't work for git.
这违背了我对 stderr 和重定向的了解;我什至编写了一个小 C 程序,在 stderr 和 stdout 上输出,以向自己证明重定向对 git 不起作用。
Fix attempt 4
修复尝试 4
In line with Jakub Nar?bski's answer, as well as replies to emails I sent to the git mailing list, --progressis the option I need. Note that this option only works after the command, and not before clone.
根据 Jakub Nar?bski 的回答,以及对我发送到 git 邮件列表的电子邮件的回复,这--progress是我需要的选项。请注意,此选项仅在命令之后有效,而在clone.
Success!
成功!
Thank you very much for all your help. This is the fixed line:
非常感谢您的帮助。这是固定线路:
git clone "" "$target" --progress > /tmp/githandler-fifo 2>&1 &


回答by Jakub Nar?bski
I think that at least some of progress reports gets silenced when output is not a terminal(tty). I'm not sure if it applies to your case, but try to pass --progressoption to 'git clone' (i.e. use git clone --progress <repository>).
我认为当输出不是终端(tty)时,至少有一些进度报告会被静音。我不确定它是否适用于您的情况,但尝试将--progress选项传递给 'git clone'(即 use git clone --progress <repository>)。
Though I don't know if it is what you wanted to have.
虽然我不知道这是不是你想要的。
回答by mvds
For one thing, the output redirection is parsed right-to-left, so
一方面,输出重定向是从右到左解析的,所以
git clone "" "$target" 2>&1 > /tmp/githandler-fifo &
is not equal to
不等于
git clone "" "$target" > /tmp/githandler-fifo 2>&1 &
The latter will redirect stderr to stdout, and then stdout (including stderr) to the file. The former will redirect stdout to the file, and then show stderr on stdout.
后者会将 stderr 重定向到 stdout,然后将 stdout(包括 stderr)重定向到文件。前者会将标准输出重定向到文件,然后在标准输出上显示标准错误。
As for piping to zenity(which I don't know), I think you may be making things overly complicated with the named pipe. Using stracemay shed some light on the inner workings of the processes you're firing up. For the inexperienced, named pipes make things worse compared to normal pipes.
至于管道zenity(我不知道),我认为您可能使命名管道使事情变得过于复杂。使用strace可能会揭示您正在启动的进程的内部工作原理。对于没有经验的人来说,与普通管道相比,命名管道会让事情变得更糟。
回答by Jonathan Leffler
Given the experiment with the FIFO called 'a', I think the problem lies in the way zenity processes its input. What happens if you type into zenity from the keyboard? (Suspicion: it behaves as you'd want, reading to EOF.) However, it could be that zenity handles terminal input (tty input) using regular blocking I/O but uses non-blocking I/O for all other device types. Non-blocking I/O is fine for input from files; it is less desirable for input from pipes or FIFOs, etc. If it did use non-blocking I/O, zenity would get the first line of output, and then exit the loop thinking it was done because its second read attempt would indicate that there was nothing else immediately available.
鉴于名为“a”的 FIFO 实验,我认为问题在于 zenity 处理其输入的方式。如果您从键盘输入 zenity 会发生什么?(怀疑:它的行为与您想要的一样,读取到 EOF。)但是,可能是 zenity 使用常规阻塞 I/O 处理终端输入(tty 输入),但对所有其他设备类型使用非阻塞 I/O。非阻塞 I/O 适用于来自文件的输入;对于来自管道或 FIFO 等的输入不太理想。如果它确实使用了非阻塞 I/O,zenity 将获得第一行输出,然后退出循环,认为它已完成,因为它的第二次读取尝试表明没有其他东西可以立即使用。
Demonstrating that this is what is happening (or not) will be tricky. I would be looking to 'truss' or 'strace' or other system call monitor to track what zenity is doing.
证明这是正在发生的事情(或没有发生)将是棘手的。我希望通过“truss”或“strace”或其他系统调用监视器来跟踪 zenity 正在做什么。
As to workarounds...if the hypothesis is correct, then you'll need to persuade zenity that it is reading from a terminal and not a FIFO, so you'll probably need to rig up a pseudo-tty (or pty); the first process would write to the master end of the pty and you'd arrange for zenity to read from the slave end of the pty. You might still use the FIFO too - though it makes a long chain of command.
至于变通方法……如果假设是正确的,那么您需要说服 zenity 它是从终端读取而不是从 FIFO 读取,因此您可能需要装配一个伪 tty(或 pty);第一个进程将写入 pty 的主端,您将安排 zenity 从 pty 的从端读取。您可能仍然会使用 FIFO - 尽管它会产生很长的命令链。

