这是我的完整代码,我使用:
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 没有附加到终端,你必须明确地告诉它,如原始答案中所述,通过指定 --progress 到 git 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'