C语言 如何使用 fork() exec() 捕获 git 克隆输出



这是我的完整代码,我使用:

    char *args[] = {"git", "clone", "https://github.com/thinker3/youdao.git", "/tmp/youdao", NULL};
    if (execv("/usr/bin/git", args) < 0) {
        perror("error on exec");
        exit(0);
    }

在子级中运行 cmd,并使用以下命令捕获输出:

    char buffer[1024];
    close(pipefd[1]);  // close the write end of the pipe in the parent
    while (read(pipefd[0], buffer, sizeof(buffer)) != 0) {
        std::cout << buffer << std::endl;
    }

在超级过程中,我得到

Cloning into '/tmp/youdao'...

但我希望得到像终端这样的输出,该怎么做?

roroco@roroco-Zhaoyang-K49 /tmp $ git clone https://github.com/thinker3/youdao.git
Cloning into 'youdao'...
remote: Counting objects: 434, done.
remote: Total 434 (delta 0), reused 0 (delta 0), pack-reused 434
Receiving objects: 100% (434/434), 221.51 KiB | 140.00 KiB/s, done.
Resolving deltas: 100% (275/275), done.
Checking connectivity... done.

更新

当然,我

可以用libgit2做到这一点,但这只是一个问题:为什么我不能得到完整的标准输出git clone

显然,

根据 git 版本,存在细微差异,因此我将描述我测试的两个版本的行为。在这两个版本之间的某个时间,行为从第一个版本更改为第二个版本。

git 版本 1.8.3

该输出中git写入stdout的唯一部分是:

Cloning into 'youdao'...

其余部分,即实际的克隆进度,仅当stderr连接到终端时,才会写入stderr。要使其始终将进度发送到 stderr ,即使它没有连接到终端(就像您的情况一样),您也必须使用 --progress .请参阅man git-clone

--进展

默认情况下,当标准错误流

连接到终端时,会在标准错误流上报告进度状态,除非指定了 -q。即使标准错误流未定向到终端,此标志也会强制进度状态。

添加此参数后,您还必须:

  • 修复从管道读取的代码(read()返回读取的字节数,并且不以 null 终止缓冲区);
  • 解析您将在stderr上获得的进度,因为它不会像您在终端中看到的那样很好地格式化。

稍后编辑

从评论来看,这似乎还不够明确,所以让我试着说得更清楚。

如果在终端中键入如下命令:

$ git clone "https://github.com/thinker3/youdao.git"

您将在终端中获得此输出:

Cloning into 'youdao'...
Receiving objects: 100%
... (rest of progress info)

如果改为键入如下命令:

$ git clone "https://github.com/thinker3/youdao.git" 1>output.log

您将在output.log中得到这个:

Cloning into 'youdao'...

在终端上:

Receiving objects: 100%
... (rest of progress info)

这意味着git写入标准输出流(已在文件中重定向)上的Cloning into...,并将进度信息写入标准错误流,该信息已保留在终端上。

相反,如果您在终端上键入如下命令:

$ git clone "https://github.com/thinker3/youdao.git" 2>output.log

您将在终端上得到这个:

Cloning into 'youdao'...

因为我们已经确定这是要去标准输出,而你把标准输出留在终端上。

但是,该文件将为空!它为空的原因由帖子开头的粗体文本描述。具体来说,只有当 stderr 要转到终端时git才会将进度信息打印到stderr。在这种情况下,stderr不会转到终端,因为您已将其重定向到文件,因此git根本不打印进度信息。

如果你想强制git将此进度信息打印到 stderr,即使 stderr 没有附加到终端,你必须明确地告诉它,如原始答案中所述,通过指定 --progressgit clone ,像这样:

$ git clone --progress "https://github.com/thinker3/youdao.git" 2>output.log

现在,将额外的参数和stderr重定向到文件,您将在文件中找到进度信息。但是,它的格式不会像您在终端上看到的那样好。这就是为什么我在最初的回答中提到,你必须解析并理解你从该管道中读到的内容。

git 版本 2.5.0

与上述描述的不同之处在于,现在输出的第一行 Cloning into 'youdao'... 也转到 stderr,并且无论 stderr 是否连接到终端,此行(与进度信息文本的其余部分相反)都会写入 stderr。其余进度信息文本与以前一样发送到 stderr,仅当错误流转到终端时。

所以现在你有了这个没有我上面几次提到的额外参数:

$ git clone "https://github.com/thinker3/youdao.git" 2>output.log
$ cat output1.log 
Cloning into 'youdao'...

这个带有那个额外的参数:

$ git clone --progress "https://github.com/thinker3/youdao.git" 2>output.log
$ cat output.log 
Cloning into 'youdao'...
remote: Counting objects: 434, done.        
remote: Total 434 (delta 0), reused 0 (delta 0), pack-reused 434        
Receiving objects: 100% (434/434), 221.51 KiB | 135.00 KiB/s, done.
Resolving deltas: 100% (275/275), done.
Checking connectivity... done.

结论

如果希望能够通过该管道获取完整的进度信息文本,则必须使用 --progress 调用git clone

原始答案中的其余观点保持不变:

  • 您必须修复管道读取代码
  • 您必须解析通过管道获得的输出,因为它与您在终端或文件中看到的输出不同。

同样与本主题相关,此处详细介绍了一种可能有用的管道 stderr 的方法

command 2>&1 >/dev/null | grep 'something'

最新更新