Collect the stdout and stderr of a command into a single file and see the output interactively:
some command 2>&1 | tee err
I use this with make so often that I have a script called mk:
make "$@" 2>&1 | tee err
(and don’t forget, we always use "$@", never $*)
The output file is named err but would probably be better named ofile (suggested by Nick B in comments). I use err in deference to 15 years of my personal Unix tradition.
The Order of Redirections
Now let’s take pipe out of the equation for a moment and consider redirections alone. On my Mac OS X laptop the command ls -d /eta /etc produces the following output:
ls: /eta: No such file or directory /etc
The “No such file or directory” message appears on stderr and “/etc” appears on stdout.
You can prove that to yourself by closing one or the other file descriptor. Play around with ls -d /eta /etc >&-, and ls -d /eta /etc 2>&- and convince yourself that this is so.
Shell redirections are processed from left to right; we can get the stderr of a command to appear on its stdout and get rid of its stdout entirely:
$ ls -d /eta /etc 2>&1 1>&- ls: /eta: No such file or directory
You might like to think about why this works. fd 2 is redirected to fd 1, meaning output to fd 2 goes to the same place as fd 1 at the time that the redirection was made. Then fd 1 is closed; this doesn’t affect the previous redirection of fd 2.
Doing it the other way round, we get this:
$ ls -d /eta /etc 1>&- 2>&1 -bash: 1: Bad file descriptor
That’s because by the time that it comes to redirect fd 2 to fd 1, fd 1 has been closed and can’t be used for a redirect.
How can we tell that 2>&1 1>&- sends what normally appears on stderr to stdout? Does this convince you:
$ ( ls -d /eta /etc 2>&1 1>&- ) | sed 's/^/*** /' *** ls: /eta: No such file or directory
By borrowing an extra fd you can even swap stderr and stdout over:
$ ls -d /eta /etc 3>&2 2>&1 1>&3 3>&- | sed 's/^/*** /' /etc *** ls: /eta: No such file or directory
Notice that the error message from ls has gone through the pipe and been transformed by sed, so it must have been sent to stdout. The normal output of ls, “/etc”, has not gone through the pipe, so must’ve been sent to stderr.
2007-04-20 at 12:08:15
Nice!
I find it’s clearer if I’m explicit about which of the two end-points of each ‘direction’ — the fd, or the stream — I am talking about. So instead of “fd 2 is redirected to fd 1″, I would say “fd 2 is redirected to the stream that fd 1 is currently directed to”. You are not actually redirecting one fd to another fd. (If I understand correctly).
2007-04-20 at 12:23:29
The name “err” in “tee err” suggests that the output is errors only; better if it was “ofile” or something.
2007-04-20 at 12:26:27
And hey, reading man sh(1) I discover that /bin/sh is tab-sensitive. I never knew. Another braindead tool.
2007-04-20 at 13:03:48
Richard, yes that seems clearer. And you are correct.
It’s all done using dup2(2). In the old days it was done using dup(2) and the knowledge that your new filedescriptor was always the lowest available positive integer.
It’s not possible (as far as I’m aware) to chain one descriptor to another.
There’s a subtlety when two descriptors refer to the same “stream” as you put it. The file pointer within that stream is shared.
2007-04-20 at 13:04:26
How is /bin/sh tab-sensitive? That does/would suck.
2007-04-20 at 21:25:56
The only think I can think of is tab-removal in here-documents introduced by <<-
2007-04-20 at 21:41:46
Never knew about
<<-(and didn’t get that far down the page before getting bored). I feel slightly ill now.2007-04-20 at 22:27:43
Yes, it’s leading tab removal in
2007-04-20 at 22:29:02
argh, <<- here documents.
2007-05-21 at 17:58:46
Note also that you can make these redirects persistent in your shell using exec without a filename. This is sometimes handy for shell scripts.
2007-05-22 at 07:47:08
Indeed. I’ve often used that useful exec feature.