Bash – output both stderr and stdout on console and store them in a file at same time

bashio-redirectionshell

may I output both stdout and stderr on console screen and store one of them into a log file?

I write a test shell script:

#!/bin/sh

echo OUT! >&1
echo ERR! >&2

I can output both of them on screen just by run the script:

$./test 
OUT!
ERR!

I can output stderr and catch stdout into log file by:

$./test | tee 1>log
ERR!

$cat log 
OUT!

I can output nothing but catch all stdout and stderro into log file by:

$./test 2>&1| tee 1>log

$cat log 
OUT!
ERR!

I can output both of stdout and stderr and catch all of them into a log file by:

$./test 2>&1 | tee log
OUT!
ERR!

$cat log 
OUT!
ERR!

I can output both can catch stdout into log file by:

$./test | tee 2>&1 log
ERR!
OUT!

$cat log 
OUT!

My questions are:

  1. how to just output stdout and catch stderr into file?(I tried ./test|tee 2>log, but doesn't work)
  2. how to just output both and catch stderr into file?

Best Answer

  1. how to just output stdout and catch stderr into file?(I tried ./test|tee 2>log, but doesn't work)
$ ./test 2>log
OUT!
$ cat log
ERR!
  1. how to just output both and catch stderr into file?
$ ./test 2>&1 >/dev/tty | tee log
OUT!
ERR!
$ cat log
ERR!

If this expression was to be part of a larger pipeline, then you may want to avoid the use of /dev/tty. One way to do that is to swap stdout and stderr. To do this swap, we need to create a third file handle like so:

$ exec 3>&1; ./test 2>&1 1>&3 | tee log; exec 3>&-
OUT!
ERR!
$ cat log
ERR!

The first statement, exec 3>&1, assigns file handle 3 to the current stdout (whatever that might be). Then, ./test 2>&1 1>&3 | tee log pipes stderr to the tee command while sending stdout to file handle 3. Finally, for good housekeeping, exec 3>&- closes file handle 3.

Additional notes and comments

Regarding:

I can output stderr and catch stdout into log file by:

$./test | tee 1>log
ERR!
$cat log 
OUT!

That can be simplified to:

$ ./test >log
ERR!
$ cat log
OUT!

Also, regarding:

I can output nothing but catch all stdout and stderro into log file by:

$ ./test 2>&1| tee 1>log
$ cat log 
OUT!
ERR!

That can be simplified to:

$ ./test >log 2>&1
$ cat log
OUT!
ERR!

Or, with bash, but not POSIX shell, a still simpler form is possible:

$ ./test &>log
$ cat log
OUT!
ERR!
Related Question