1

here is my full code, I use:

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

to run cmd in child, and capture output with:

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

in super process, I get

Cloning into '/tmp/youdao'...

but I hope get output like terminal, how to do that?

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.

update

of course, I can do it with libgit2, but it just a question: why I can't get git clone full stdout

iufachajov
  • 233
  • 2
  • 8
  • @pikkewyn, it's worse, it can out full output, but `read(pipefd[0]` can't capture it, my target is run cmd like `system(cmd)`(out line by line) and I can capture its stdout and stderr – iufachajov Feb 29 '16 at 15:08

2 Answers2

3

Apparently there are minor differences depending on the git version, so I will describe the behavior of the two versions I tested with. Sometime between these two versions the behavior changed from the first to the second.

git version 1.8.3

The only part from that output that git writes to stdout is this:

Cloning into 'youdao'...

The rest of it, the actual cloning progress, gets written to stderr and only if stderr is attached to a terminal. To make it always send the progress to stderr, even when it is not attached to a terminal (like in your case), you have to call it with --progress. See man git-clone:

--progress

Progress status is reported on the standard error stream by default when it is attached to a terminal, unless -q is specified. This flag forces progress status even if the standard error stream is not directed to a terminal.

After you add this parameter you will also have to:

  • fix the code that reads from the pipe (read() returns the number of bytes read and doesn't null-terminate the buffer);
  • parse the progress you'll get on stderr, because it will not be nicely formatted as you see it in the terminal.

Later Edit

Judging from the comments, it appears this wasn't explicit enough, so let me try to say it more clearly.

If you type the command like this in a terminal:

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

you will get this output in the terminal:

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

If you type the command like this instead:

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

you will get this in output.log:

Cloning into 'youdao'...

and this on the terminal:

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

This means that git writes Cloning into... on the standard output stream (which you have redirected in the file) and the progress information on the standard error stream, which you have left on the terminal.

If instead you type the command like this on the terminal:

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

you will get this on the terminal:

Cloning into 'youdao'...

because we already established this is going to stdout, and you left stdout on the terminal.

The file will however be empty! The reason it is empty is described by the bold text at the beginning of the post. Specifically, git prints progress information to stderr only if stderr is going to a terminal. In this case stderr is not going to a terminal because you have redirected it to a file, so git doesn't print the progress information at all.

If you want to force git to print this progress information to stderr even if stderr is not attached to a terminal, you have to tell it explicitly, as described in the original answer, by specifying --progress to git clone, like this:

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

Now, with the extra parameter and stderr redirected to the file, you will find the progress information in the file. It will however not be as nicely formatted as you see it on the terminal. That's why I mentioned in my original answer that you will have to parse and make sense of what you read from that pipe.

git version 2.5.0

The difference to the above description is that now the first line of output, Cloning into 'youdao'..., also goes to stderr and this line (as opposed to the rest of the progress info text) gets written to stderr no matter if stderr is attached to a terminal or not. The rest of the progress info text is sent to stderr the same as before, only if the error stream goes to a terminal.

So now you have this without the extra parameter I mentioned a few times above:

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

and this with that extra parameter:

$ 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.

Conclusion

If you want to be able to get the full progress information text through that pipe, you have to call git clone with --progress.

The rest of the points from the original answer remain the same:

  • you will have to fix your pipe reading code
  • you will have to parse the output you get through the pipe because it will not be the same as what you see on the terminal or in the file.
Ionut
  • 6,436
  • 1
  • 17
  • 17
  • I think "The only part from that output that git writes to stdout" is wrong, only when I comment `dup2(pipefd[1], 1);`(don't change stdout, redirect stderr to pipe), I can get `Cloning into 'youdao'...`, that means the `Cloning into 'youdao'...` is in stderr, but the weird thing is why other output doesn't show – iufachajov Feb 29 '16 at 15:45
  • No, it means that string is sent by the child directly to `stdout`, without going through your pipe. And the bold part in the answer tells you exactly why the rest of the output doesn't show. You can try this in a terminal and see what you get on the terminal and what you get in the file: `git clone ... 2>>err_output.txt` – Ionut Feb 29 '16 at 15:57
  • I means "Cloning into 'youdao'..." originally belongs stderr, right? I try `git clone "https://github.com/thinker3/youdao.git" 1>a.log` and `git clone "https://github.com/thinker3/youdao.git" 2>a.log` and both I cannot get rest output(like Receiving objects: 100%) , where is rest output?(stdout or stderr or other io)? – iufachajov Mar 01 '16 at 03:03
  • I've edited the answer and made it more explicit, it was too much to fit into a comment. I hope it's more clear now. – Ionut Mar 01 '16 at 06:47
  • lonut, I'm very sure when i use `git clone "https://github.com/thinker3/youdao.git" 1>output.log`, the output.log is empty(I try it in linux guake and linux default terminator), here is [steps](https://gist.github.com/bfe8897c813c679388d1), my git version is 1.9.1 – iufachajov Mar 01 '16 at 08:36
  • I commented on that link with my output. I'll try to update my git to the same version as yours and try again. – Ionut Mar 01 '16 at 09:23
  • I tested with a newer `git` and apparently there is a minor difference in it's output. It doesn't affect the output too much, but I've updated the answer to describe it. Hope it helps you. The final fix remains exactly the same one I mentioned in the first version of the answer: an extra parameter to `git clone`. – Ionut Mar 01 '16 at 19:02
0

Also related to this topic, a way to pipe stderr that could be useful is explained with detail here

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