Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question

3 Answers 3

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
share|improve this answer
    
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

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.

share|improve this answer
    
I think your second tee is missing a redirect to stderr. It should be: tee -a log >&2 3>&- –  richvdh Dec 4 at 12:26
    
Thanks @richvdh. Fixed, –  Stéphane Chazelas Dec 4 at 12:30

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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