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.

How can I use BASH magic to achieve this?
I want to only see stderr output on the screen,
but I want both stdout and stderr to be written to a file.

Clarification: I want both stdout and stderr to end up in the same file. In the order they happen.
Unfortunately none of the answers below does this.

share|improve this question
    
A new go at the same fundamental problem: How to capture ordered STDOUT/STDERR and add timestamp/prefixes? –  Gilles Sep 26 at 22:57

5 Answers 5

Even without any redirection, or with nothing but >logfile 2>&1, you're not guaranteed to see output in order of generation.

For starters, the stdout from the application will be line-buffered (to tty) or buffered (to a pipeline) but stderr is unbuffered, so relationships between the order of output are broken as far as a reader is concerned. Subsequent stages in any pipeline you could concoct will not get deterministically ordered access to the two streams (they are conceptually things happening in parallel, and you're always subject to the scheduler - if by the time your reader gets a slice the writer has already written to both pipes, you cannot tell which came first).

"[T]he order they happen" is only really known to the application. Ordering of output across stdout/stderr is a well-known - classic, perhaps - problem.

share|improve this answer

I have been able to accomplish this by placing the following line at the top of a bash script:

exec 1>>log 2> >(tee -a log >&2)

This will redirect stdout to the file log (1>>log), then tee stderr to the file log (2> >(tee -a log) and direct it back to stderr (>&2). In this way, I get a single file, log, which shows both stdout and stderr in order, and stderr is also displayed on the screen as usual.

The catch is that it only seems to work when I append to the file. If I don't append, it appears that the two redirections clobber each other, and I only get whichever one outputs last.

share|improve this answer

When you use the construction: 1>stdout.log 2>&1 both stderr and stdout get redirected to the file because the stdout redirection is set up before the stderr redirection.

If you invert order you can get stdout redirected to a file and then copy stderr to stdout so you can pipe it to tee.

$ cat test
#!/bin/sh
echo OUT! >&1
echo ERR! >&2

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

$ cat stdout.log
OUT!

$ cat stderr.log
ERR!
share|improve this answer
    
+1 good answer. –  glenn jackman Mar 19 '11 at 15:10
    
That doesn't let you log the two streams to the same file. –  artistoex Mar 19 '11 at 16:19
    
@artistoex: You just can use tee -a with the same file used in 1> to sum both outputs in the same file. Something like: ./test 2>&1 1>out+err.log | tee -a out+err.log –  mmoya Mar 19 '11 at 17:41
    
I have no idea why, but this does not work with wget -O - www.google.de for me. (compare with solution below) –  artistoex Mar 19 '11 at 17:56
1  
Using tee -a won't work reliably to have the same output that would be on the console if nothing was redirected. The output that goes via tee might be slightly delayed compared with the output that comes directly from the command, and so might appear later in the log. –  Gilles Mar 19 '11 at 20:33

You want to duplicate the error stream so that it appears both on the console and in the log file. The tool for that is tee, and all you need to do is apply it to the error stream. Unfortunately, there's no standard shell construct to pipe a command's error stream into another command, so a little file descriptor rearrangement is required.

{ { echo out; echo err 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^   ^^^^^^^^^^^^    ^^^^^^^^^
  command produces output      stdout→3                    →log
  command produces error       stderr→1   dup to terminal  →log
share|improve this answer
    
This also, does not quite work for me, I get stdout lines first, and then all stderr lines in 'log' file. example: { { echo out; echo err 1>&2;echo out2;echo err2 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1 –  Andreas Mar 19 '11 at 20:56
1  
@Andreas: Oh, duh, tee introduces a delay here too. I don't think there is a pure shell solution. In fact I wonder whether there is a solution without modifying the application in some way. –  Gilles Mar 19 '11 at 21:28

Let f be the command you'd like be executed, then this

( exec 3>/tmp/log; f 2>&1 1>&3 |tee >(cat)>&3 )

should give you what you wish. For example wget -O - www.google.de would look like this:

( exec 3>/tmp/googlelog; wget -O - www.google.de 2>&1 1>&3 |tee >(cat)>&3 )
share|improve this answer
    
This almost works for me, but in the log file I first get all the stdout lines, and then all stderror lines. I want them to be interleaved in the natural order as they happen. –  Andreas Mar 19 '11 at 20:44

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.