Search code examples
bashtimepipeio-redirectiontee

How to capture stderr and stdout AND timing info with tee including SIGINT?


Suppose I have a program foo which prints to both stderr and stdout. I want to be able to time it and tee the output to a log file.

Program Description

I can call foo as follows:

user$ time ./foo

This gives (for example) output on two streams:

stdout:

stdout says hi

stderr:

stderr says hi

Combining stderr and stdout

I can combine them easily enough to standard output using BASH I/O redirection:

user$ ./foo 2>&1

This gives the following output:

stdout:

stdout says hi
stderr says hi

stderr: (empty!)

Using tee

I can capture the combined output of the program using tee:

user$ ./foo 2>&1 | tee log.txt

stdout:

stdout says hi
stderr says hi

stderr: (empty!)

log.txt:

stdout says hi
stderr says hi

Capturing Timing using time

I can time the execution of my program using BASH's builtin time command while combining stdout and stderr:

user$ time ./foo 2>&1

stdout:

stdout says hi
stderr says hi

stderr::

real    0m0.017s
user    0m0.003s
sys     0m0.004s

I can capture the output of the time command as described here and redirect it to a file:

user$ ( time ./foo ) > log.txt

stdout: (empty!)

stderr:

stderr says hi

real    0m0.017s
user    0m0.003s
sys     0m0.004s

log.txt: stdout says hi

Combining I/O, Capturing time's output, AND piping to tee

How can this be done? I'm imagining something like the following:

( time ./foo 2&>1 ) | tee log.txt

stdout: (empty!)

stdout says hi
stderr says hi

real    0m0.017s
user    0m0.003s
sys     0m0.004s

stderr: (empty!)

log.txt:

stdout says hi
stderr says hi

real    0m0.017s
user    0m0.003s
sys     0m0.004s

Doing the following works, except when I Ctrl-C (SIGINT) out of the program prematurely.

( time ./foo ) 2&>1 | tee log.txt

How can I get it to work when I Ctrl-C as well?


Solution

  • myfunc() { echo stdout; echo stderr >&2; sleep 3; }
    { time myfunc; } > >(tee log.txt) 2>&1
    

    ...emits content including time metadata to log.txt, even when cancelled with Ctrl+C.

    See Write tcpdump output to compressed / gziped file for a more-in-depth discussion of the same problem, including an answer intended for scripts as opposed to interactive interpreters.


    For the program foo in your example, you could do the following:

    { time ./foo; } > >(tee log.txt) 2>&1