50

Let us say I run a command or shell script, and it gives me output. Without knowing the internals of this command or shell script, how does one determine if is the output was from stderr or stdout?

For e.g.,

$ ls -ld /
drwxrwxr-t  35 root  admin  1258 Dec 11 19:16 /

vs

ls -ld /test
ls: /test: No such file or directory

How do I ascertain that the first command printed to stdout and the second to stderr (did it?)?

3
  • 2
    What problem are you trying to solve here?
    – Kenster
    Feb 23, 2015 at 22:20
  • 7
    Was afraid someone would ask that; none really, mostly curious and hoping to improve my understanding of redirection.
    – KM.
    Feb 23, 2015 at 22:22
  • 7
    You could put stderred in your shell environment's LD_PRELOAD to get stdout and stderr in different colours. Here's a related question in that vein.
    – Anko
    Feb 24, 2015 at 11:26

8 Answers 8

32

There's no way to tell once the output has already been printed. In this case, both stdout and stderr are connected to the terminal, so the information about which stream was written to was already lost by the time the text appeared on your terminal; they were combined by the program before ever making it to the terminal.

What you can do, in a case like the above, would be to run the command with stdout and stderr redirected to different places and see what happens. Or run it twice, once with stdout redirected to /dev/null and once with stderr redirected to /dev/null, and see which of those cases results in the text showing up.

You can redirect stdout to /dev/null by tacking >/dev/null on the end of the command line, and you can redirect stderr to /dev/null by adding 2>/dev/null.

21

You can redirect stdout using > file, and redirect stderr using 2> file. Many modern shells support redirecting to commands, so you can use sed to highlight which output comes from which stream:

$ ls 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
1: unity_support_test.0
1: vmwareDnD

$ ls foo 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
2: ls: cannot access foo: No such file or directory
3
  • Very nice! It would be great if one could highlight the lines in colors rather than prefix.
    – dotancohen
    Feb 24, 2015 at 8:57
  • 5
    @dotancohen: One can! Eg, (echo "this is stdout"; echo "this is stderr" >&2) > >(sed 's/.*/\x1b[32m&\x1b[0m/') 2> >(sed 's/.*/\x1b[31m&\x1b[0m/')
    – PM 2Ring
    Feb 24, 2015 at 11:30
  • Note that if there's mixed output from stdout and stderr, first stdout will be printed, then stderr.
    – alexia
    Feb 24, 2015 at 14:51
6

The annotate-output script from Debian's devscripts lets you do this selectively:

$ annotate-output ls -ld /test
14:54:22 -: Started ls -ld /test
14:54:22 E: ls: cannot access /test: No such file or directory
14:54:22 -: Finished with exitcode 2

The second column indicates stdout and stderr with O and E respectively.

There are some caveats, the main one being as noted in the other answers: you can't do this after the fact. Neither the shell nor the terminal are aware of how an arbitrary program uses its file descriptors, though the shell is responsible for setting them up initially.

This method uses fifos, writing to a fifo can behave differently than writing to a tty, and writing to two different fifos is definitely different (potential timing/interleaving issues). Also, it's not suitable for interactive use, e.g. annotate-output bash is not a great plan, but it's useful for many other purposes. There are many, many examples of scripts and shell functions in answers to related questions about colorising stdin/stdout/stderr, the most robust is stderrd which uses runtime modification of (most) programs to modify data written to stderr.

This question that Anko links to has good answers on that related theme: colorising the stdout/stderr output: Can I configure my shell to print STDERR and STDOUT in different colors?

1
  • 2
    Note that it's a bash script using while read loops and running one date command for each line of stdout or stderr, so it going to be a lot less efficient than the cmd > >(ts '%T O:') 2> >(ts '%T E:') equivalent. Sep 15, 2017 at 9:19
1

Apart from the other answers, it is interessant to point to /proc/$PID/fd (although it does not answer the question):

$ cat > /dev/null 2> /tmp/blablah &
[1] 3073

[1]+  Stopped                 cat > /dev/null 2> /tmp/blablah
$ ls -l /proc/3073/fd
total 0
lrwx------ 1 kampde kampde 64 Feb 24 11:43 0 -> /dev/pts/33
l-wx------ 1 kampde kampde 64 Feb 24 11:43 1 -> /dev/null
l-wx------ 1 kampde kampde 64 Feb 24 11:43 2 -> /tmp/blablah

As you see, here you can see the file descriptors opened for a process. 0 is the STDIN, 1 is the STDOUT and 2 is the STDERR. If you didn't have the STDOUT or STDERR redirected, you'd see /dev/pts/33 (in this example at least) because they would point to the terminal.

notes: /proc/$PID only exists for running processes. In this case I have used cat without arguments so it does not end until I close the STDIN. I have also executed it in the background so I have the PID immediately for the sake of this example.

1

Usually STDERR will have the program name prepended to the message with a colon.

Example:

rpm -zq some_utils 
rpm: -zq: unknown option

Vs

rpm -ql some_utils 
package some_utils is not installed
1

You can make use of grep for this very easily:

$ ls -l test | grep .
ls: cannot access 'test': No such file or directory

This assumes that your grep output is coloured (this is the default on most distros).

This will result in stdout being highlighted with the default grep colour, for example:

$ (echo "this is stdout"; echo "this is stderr" >&2) | grep .
this is stderr
this is stdout

The first line of output is white while the second is coloured. This might be the opposite of what you want if your default grep colour is red. If you explicitly want to set stdout to green you could do this to set the grep colours to green:

$ GREP_COLORS='ms=01;32'
$ (echo "this is stdout"; echo "this is stderr" >&2) | grep .

If you explicitly want to get red output for stderr:

$ GREP_COLORS='ms=01;31'
$ (echo "this is stdout"; echo "this is stderr" >&2) 2> >(grep .)

If you really want both stderr and stdout to be specific colours then you can do this:

$ (echo "this is stdout"; echo "this is stderr" >&2) 2> >(GREP_COLORS='ms=01;31' grep .) 1> >(GREP_COLORS='ms=01;32' grep .)
0

It is not quite clear what you are asking but this might help

ls -ld /
echo $?    # Exit status 0 returned because command executed successfully.

ls -ld /test
echo $?    # Non-zero exit status returned -- command failed to execute

Source

If exit code is 0 it simply means that command is executed correctly (stdout), here you can find meanings if exit code is different than 0 (stderr)

3
  • Very interesting! I would've never connected exit statuses with redirection. Does zero imply stdout and non-zero imply stderr?
    – KM.
    Feb 23, 2015 at 22:25
  • 7
    @KM., no. I'd say it strongly correlates, but there's nothing that stops a program from writing to stderr and returning a good error code, or writing to stdout and returning a bad error code. In fact, try find /root - assuming you're not running as root, you should get 2 lines printed out - "/root" gets printed to stdout, and "find: /root: Permission denied" gets printed to stderr. And find returns a bad return code.
    – godlygeek
    Feb 23, 2015 at 22:37
  • 1
    op's problem is clear that how to tell which is stdout or stderr.
    – showkey
    Jul 25, 2017 at 12:42
-1

To capture and test for error output:

ls -l test 2>errors
if [ -s errors ]; then echo "There were errors:" && cat errors; fi
rm errors

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .