git stderr输出无法管道

时间:2010-10-31 11:08:31

标签: git bash pipe named-pipes zenity

我正在使用bash和zenity为git://链接编写图形URI处理程序,并且我正在使用zenity'text-info'对话框来显示git在运行时的克隆输出,使用FIFO管道。这个脚本长约90行,所以我不打算在这里发帖,但这里是最重要的一行:

git clone "$1" "$target" 2>&1 | cat >> /tmp/githandler-fifo &
cat /tmp/githandler-fifo | zenity --text-info --text='Cloning git repository' &

我正在使用FIFO而不是直接管道来允许它们异步运行并允许在zenity窗口关闭时终止git。

问题是,git输出中出现的唯一一行是第一行:

Initialized empty Git repository in /home/delan/a/.git/

带有计数对象等的其他行不会显示或显示在终端上。

当前原因

目前关于为什么这不起作用的共识似乎是cat是非阻塞的并且在第一行之后退出,只是将其传递给zenity而不是其他。我的目标是强制阻止读取,并让zenity的文本信息对话框逐步显示所有输出。

git在stderr上输出进度消息(除了“Initialized”消息之外的任何内容),但是当我尝试将stderr管道传输到文件或与stdout合并时,消息就会消失。

修复尝试1

我试图在C,面包和bwrite中编写cat函数的两个阻塞版本,如下所示:

#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);
    }
}

它们很好地工作,因为它们阻止并且不会退出EOF,但它还没有完全解决问题。目前,使用一个,另一个或两者,在理论上起作用,但在实践中,zenity现在根本没有显示。

修复尝试2

@mvds建议使用常规文件与tail -f而不是cat结合使用,可以这样做。对这样一个简单的解决方案感到惊讶(谢谢!)我尝试过但不幸的是,只有第一行出现在zenity而没有别的。

修复尝试3

在做了一些strace'ing并检查git的源代码之后,我意识到git在stderr上输出了它的所有进度信息(任何超过“Initialized”消息的信息),以及这是第一行和我的假设它是因为猫早期退出EOF是一个巧合/错误的假设(git在程序结束前不会EOF)。

情况似乎变得更加简单,因为我不应该从原始代码(在问题的开头)改变任何东西,它应该工作。然而,神秘的是,当重定向时,stderr输出“消失” - 这只是在git中发生的事情。

测试用例?试试这个,看看你是否在文件中看到任何内容(你不会):

git clone git://anongit.freedesktop.org/xorg/proto/dri2proto 2> hurr

这违背了我对stderr和重定向的所有了解;我甚至编写了一个在stderr和stdout上输出的小程序来向自己证明重定向对git不起作用。

修复尝试4

根据JakubNarębski的回答,以及对我发送到git邮件列表的电子邮件的回复,--progress是我需要的选项。请注意,此选项仅在命令之后有效,而不是在clone之前。

成功!

非常感谢你的帮助。这是固定的:

git clone "$1" "$target" --progress > /tmp/githandler-fifo 2>&1 &

3 个答案:

答案 0 :(得分:18)

我认为当输出不是终端(tty)时,至少有一些进度报告会被静音。我不确定它是否适用于您的情况,但尝试将 --progress 选项传递给'git clone'(即使用git clone --progress <repository>)。

虽然我不知道这是不是你想要的。

答案 1 :(得分:6)

首先,输出重定向从右向左解析,所以

git clone "$1" "$target" 2>&1 > /tmp/githandler-fifo &

不等于

git clone "$1" "$target" > /tmp/githandler-fifo 2>&1 &

后者会将stderr重定向到stdout,然后将stdout(包括stderr)重定向到该文件。前者将stdout重定向到文件,然后在stdout上显示stderr。

至于zenity的管道(我不知道),我认为你可能会因为命名管道过于复杂。使用strace可以阐明你正在开火的过程的内部运作。对于没有经验的命名管道,与普通管道相比会使事情变得更糟。

答案 2 :(得分:1)

鉴于FIFO的实验称为'a',我认为问题在于zenity处理其输入的方式。如果从键盘输入zenity会发生什么? (怀疑:它的行为与你想要的一样,读取到EOF。)但是,可能是zenity使用常规阻塞I / O处理终端输入(tty输入),但对所有其他设备类型使用非阻塞I / O.非阻塞I / O适用于文件输入;它不太适合来自管道或FIFO等的输入。如果它确实使用非阻塞I / O,zenity将获得第一行输出,然后退出循环认为它已完成,因为它的第二次读取尝试将指示没有别的东西可以立即使用。

证明这是正在发生的事情(或不发生)将是棘手的。我希望'桁架'或'strace'或其他系统调用监视器来跟踪zenity正在做什么。

至于解决方法......如果假设是正确的,那么你需要说服它从终端而不是FIFO中读取zenity,所以你可能需要装备一个伪tty(或者PTY);第一个进程将写入pty的主端,并且您将安排zenity从pty的slave端读取。您可能仍然使用FIFO - 尽管它会产生一长串命令。