13

我正在为带有 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 上输出进度消息(除了“初始化”消息之外的任何内容),但是当我尝试将 stderr 传输到文件或与 stdout 合并时,消息消失了。

修复尝试 1

我尝试用 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);
    }
}

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

修复尝试 2

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

修复尝试 3

在做了一些 strace'ing 并检查了 git 的源代码之后,我意识到 git 在 stderr 上输出了它的所有进度信息(任何超出“初始化”消息的内容),事实上这是第一行,我假设这是因为 cat在 EOF 早期退出是一个巧合/错误的假设(git 直到程序结束才 EOF)。

情况似乎变得简单多了,因为我不应该对原始代码进行任何更改(在问题的开头),它应该可以工作。然而,奇怪的是,stderr 输出在重定向时会“消失”——这只是 git 中发生的事情。

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

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

这与我所知道的关于标准错误和重定向的一切背道而驰;我什至编写了一个在 stderr 和 stdout 上输出的小 C 程序,以向自己证明重定向对 git 不起作用。

修复尝试 4

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

成功!

非常感谢您的帮助。这是固定线路:

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

4

3 回答 3

19

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

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

于 2010-10-31T15:13:19.087 回答
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可能会揭示您正在启动的过程的内部工作原理。对于没有经验的人来说,命名管道比普通管道更糟糕。

于 2010-10-31T12:10:36.933 回答
1

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

证明这是正在发生的(或没有发生)将是棘手的。我会寻找 'truss' 或 'strace' 或其他系统调用监视器来跟踪 zenity 正在做什么。

至于解决方法……如果假设是正确的,那么您需要说服 zenity 它是从终端读取而不是从 FIFO 读取的,因此您可能需要安装一个伪 tty(或 pty);第一个进程将写入 pty 的主端,您将安排 zenity 从 pty 的从端读取。您也可能仍然使用 FIFO——尽管它会产生很长的命令链。

于 2010-10-31T18:12:31.283 回答