4

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?
6
  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!
5

Shells redirections should be fairly enough. First, to redirect stderr to a file...

$ ./test 2> myfile.txt

Here, stdout remains the screen, since it wasn't redirected. myfile.txt will contain ERR.

Then, if you want to output both, and still catch stderr into a file, you'll probably have to go in two steps with a little command substitution...

$ ./test 2> >(tee myfile.txt >&2)

This will send the error stream to the tee process, which will reprint them. stdout remains untouched. For more information, have a look at this Stack Overflow question.

Similarly, if you want to catch stdout into a file, and leave stderr untouched...

$ ./test > >(tee myfile.txt)
  • +1 for not involving tty. It is more flexible if we want to use it in a pipeline. – ctrl-alt-delor Jan 4 '15 at 12:10

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.