8

Using bash, how do I copy stderr and stdout to a log file and also leave them displayed on the console?

I would like to do this within the script itself using an exec.

I tried with

exec &>> log.out

echo "This is stdout"
echo "This is stderr" >&2

But the above prints nothing on the console. How can I achieve this in bash?

8

You are looking for tee.

See man tee for details.

To combine it with exec, you have to use process substitution. (See man bash for details.)

exec &> >(tee  log.out)
echo "This is stdout"
echo "This is stderr" >&2
  • I have looked at it. Doing exec 2>&1 | tee -a log.out only prints on the console, nothing in the log file. Maybe I am not using the right syntax? – adarshr Mar 12 '13 at 11:58
  • @adarshr Please double-check your code. I have writen about tee in combination with process substitution. – H.-Dirk Schmitt Mar 12 '13 at 12:34
  • 1
    That merges stdout and stderr though and can have the same kind of side-effect as mentioned in my answer. – Stéphane Chazelas Mar 12 '13 at 13:05
  • @StephaneChazelas Look at the example code in the question - that was the intent from the OP. – H.-Dirk Schmitt Mar 12 '13 at 13:46
  • What I mean by merge is that now the script's errors go to stdout. So if someone does the-script | wc -l for instance, that will also count the error lines and you won't see the errors. – Stéphane Chazelas Mar 12 '13 at 14:18
4

You can do:

: > log # empty log file if necessary
{ { {

  ...the script

} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log >&2 3>&-
} 3>&1
exit "${PIPESTATUS[0]}"

You could also write it as:

: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)

...the script

But because bash is not waiting for those processes started with >(...), that has the nasty effect of sometimes outputting something to the terminal after the command has returned which can have even nastier effects (like silently discarding that output) if the terminal "tostop" attribute is on.

In any case, by making stdout a pipe in both solutions, and because two commands independently output the output and error messages, this will affect output buffering and the order the output and error messages are displayed.

  • I think your second tee is missing a redirect to stderr. It should be: tee -a log >&2 3>&- – richvdh Dec 4 '14 at 12:26
4

I know this is an old post, but why not just do this?

echo "hi" >> log.txt #stdout -> log
echo "hi" | tee -a log.txt #stdout -> log & stdout
echo "hi" &>> log.txt #stdout & stderr -> log
echo "hi" |& tee -a log.txt #stdout & stderr -> log & stdout

And of course, if you want stdout you can just print regularly.

You can do this with any combination of streams you wish, just using those two basic commands.

I know I came here and did not get an easy to understand/implement answer, hopefully this will be help to someone else who is struggling.

By the way, for noobs out there like my previous self, all the tee command does is output the stdin input to both stdout and the file(s) specified as subsequent arguments. -a stands for append, so you don't overwrite the file with every use of the command. If you have further questions, I find this to be a very helpful resource for quickly learning bash.

  • 1
    Thanks for contributing. I on the other hand, have totally forgotten what I was trying to do :-) – adarshr Aug 1 '17 at 20:02
  • 1
    +1 for giving multiple, nice and simple, tee examples. – Tim May 31 '18 at 10:55
1

One more way of doing it is using redirections within functions.

#!/bin/bash

function1 () {
    echo 'STDOUT from function 1'
    echo 'STDERR from function 1' >&2
}

function2 () {
    echo 'STDOUT from function 2'
    echo 'STDERR from function 2' >&2
}


function3 () {
    echo 'STDOUT from function 3'
    echo 'STDERR from function 3' >&2
}

main() {
    function1
    function2
    function3
}

main 2>&1 |tee log.txt

Here we have a main function which invokes all other functions. Now redirecting STDOUT and STDERR of main function to tee.

  • IMHO this is the cleanest way of doing this, at least for simple cases. Thanks. – ACK_stoverflow Dec 3 '16 at 21:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.