Reputation: 15286
How can I "flush" the stdout
of the exec
command to my script's stdout
without "waiting" for the resulting exec to return?
For instance in the following script I would like the git clone
output to appear immediately in my script context:
#!/usr/bin/tclsh
# git outputs progress of the clone download but it isn't visible in this script's context. How can I flush it?
exec git clone /path/to/some/repo.git
I'm guessing I need some sort of combination of pipe "|"
and tee
and file redirection.
Can't seem to get it right.
Upvotes: 2
Views: 2067
Reputation: 31
If you are not interested in the output of the program in your TCL-program, you could also just redirect the output with the '>@' option of the exec command to a different location, i.e. '>@ stdout' in your case.
Upvotes: 2
Reputation: 137717
To get the output immediately, you need to open the subcommand as a pipeline. The correct (and not quite obvious, for which we apologise) way to do that is with this construction of open |[list …]
:
set mypipeline [open |[list git clone /path/to/some/repo.git]]
# Now to read it by line; we'll just print it out with a line number to show off
set linenum 0
while {[gets $mypipeline line] >= 0} {
puts "Line [incr linenum]: $line"
}
# Close the pipe now we're at EOF
close $mypipeline
Be aware however that some programs (I don't know if git
is one) change their behaviour when run in a pipeline, buffering their output until they have a full buffer's worth. (It's part of how the C runtime works by default when output isn't to a terminal.) If that's a problem, you will have to run using Expect. That's a large enough topic that you should look for (and ask if necessary) a separate question; it's quite a step change in complexity, alas.
Also be aware that git might well write to its standard error (as noted in this question, Reading output of git push from PHP exec() function) so you might need to merge standard error into the captured standard out (as tersely documented on the exec
manual page).
set output [exec git clone /path/to/some/repo.git 2>@1]
set mypipeline [open |[list git clone /path/to/some/repo.git 2>@1]]
# ...
It's possible to do read/write pipes as well, but rather more complex.
Upvotes: 5
Reputation: 247042
exec
's job is to capture the output of the command, so I don't think you can alter the buffering of it. Here's a test best illustrated in an interactive tclsh session: both these commands wait until the exec'ed process is complete before returning the output, even the one where I've explicitly asked for line buffering
exec sh -c {sleep 2; echo a; sleep 2; echo b; sleep 3; echo c}
exec stdbuf --output=L sh -c {sleep 2; echo a; sleep 2; echo b; sleep 3; echo c}
You're going to have to open a pipe to the command, then read the output line by line. This prints the output of the above command when it appears:
set pipe [open [list "|" sh -c {sleep 2; echo a; sleep 2; echo b; sleep 3; echo c}] r]
while {[gets $pipe line] != -1} {puts $line}
close $pipe
What do you mean when you say it "isn't visible in this script's context"? Recall that exec
captures the output. It isn't printed by default. I wonder if you just want
puts [exec git clone /path/to/some/repo.git]
But if you want to see the text in "real-time", open a pipe and loop over gets
as demonstrated.
Upvotes: 0