linux shell 屏幕显示stdout stderr的同时 分别重定向到文件

I'm assuming you want to still see STDERR and STDOUT on the terminal. You could go for Josh Kelley's answer, but I find keeping a tail around in the background which outputs your log file very hackish and cludgy. Notice how you need to keep an exra FD and do cleanup afterward by killing it and technically should be doing that in a trap '...' EXIT.

There is a better way to do this, and you've already discovered it: tee.

Only, instead of just using it for your stdout, have a tee for stdout and one for stderr. How will you accomplish this? Process substitution and file redirection:

command > >(tee stdout.log) 2> >(tee stderr.log >&2)

Let's split it up and explain:

> >(..)

>(...) (process substitution) creates a FIFO and lets tee listen on it. Then, it uses > (file redirection) to redirect the STDOUT of command to the FIFO that your first tee is listening on.

Same thing for the second:

2> >(tee stderr.log >&2)

We use process substitution again to make a tee process that reads from STDIN and dumps it intostderr.logtee outputs its input back on STDOUT, but since its input is our STDERR, we want to redirect tee's STDOUT to our STDERR again. Then we use file redirection to redirect command's STDERR to the FIFO's input (tee's STDIN).

See http://mywiki.wooledge.org/BashGuide/InputAndOutput

Process substitution is one of those really lovely things you get as a bonus of choosing bash as your shell as opposed to sh (POSIX or Bourne).


In sh, you'd have to do things manually:

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee stdout.log < "$out" &
tee stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

6. Miscellaneous Operators

Aside from the standard I/O operators, bash also provides a few more advanced operators that make life on the shell that much nicer.

6.1. Process Substitution

A cousin of the pipe is the process substitution operator, which comes in two forms: <() and >(). It's a convenient way to use named pipes without having to create temporary files. Whenever you think you need a temporary file to do something, process substitution might be a better way to handle things.

What it does, is basically run the command inside the parentheses. With the <() operator, the command's output is put in a named pipe (or something similar) that's created by bash. The operator itself in your command is replaced by the filename of that file. After your whole command finishes, the file is cleaned up.

Here's how we can put that into action: Imagine a situation where you want to see the difference between the output of two commands. Ordinarily, you'd have to put the two outputs in two files and diff those:

$ head -n 1 .dictionary > file1
$ tail -n 1 .dictionary > file2
$ diff -y file1 file2
Aachen                                                        | zymurgy
$ rm file1 file2

Using the Process Substitution operator, we can do all that with a one-liner and no need for manual cleanup:

$ diff -y <(head -n 1 .dictionary) <(tail -n 1 .dictionary)
Aachen                                                        | zymurgy

The <(..) part is replaced by the temporary FIFO created by bash, so diff actually sees something like this:

$ diff -y /dev/fd/63 /dev/fd/62

Here we see how bash runs diff when we use process substitution. It runs our head and tail commands, redirecting their respective outputs to the "files"/dev/fd/63 and /dev/fd/62. Then it runs the diff command, passing those filenames where originally we had put the process substitution operators.

The actual implementation of the temporary files differs from system to system. In fact, you can see what the above would actually look like to diff on your box by putting an echo in front of our command:

$ echo diff -y <(head -n 1 .dictionary) <(tail -n 1 .dictionary)
diff -y /dev/fd/63 /dev/fd/62

The >(..) operator is much like the <(..) operator, but instead of redirecting the command's output to a file, we redirect the file to the command's input. It's used for cases where you're running a command that writes to a file, but you want it to write to another command instead:

$ tar -cf >(ssh host tar xf -) .


zz:http://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe


你可能感兴趣的:(linux shell 屏幕显示stdout stderr的同时 分别重定向到文件)