32

According to "Linux: The Complete Reference 6th Edition" (pg. 44), you can pipe only STDERR using the |& redirection symbols.

I've written a pretty simple script to test this:

#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2

I run this script like this:

./script.sh |& sed 's:^:\t:'

Presumably, only the lines printed to STDERR will be indented. However, it doesn't actually work like this, as I see:

    Normal Text.
    Error Text. 

What am I doing wrong here?

3 Answers 3

36

I don't know what text your book uses, but the bash manual is clear (if you're a little familiar with redirections already):

If |& is used, command1’s standard error, in addition to its standard output, is connected to command2’s standard input through the pipe; it is shorthand for 2>&1 |. This implicit redirection of the standard error to the standard output is performed after any redirections specified by the command.

So if you don't want to mix standard output and standard error, you'll have to redirect standard output somewhere else. See How to grep standard error stream (stderr)?

{ ./script.sh 2>&1 >&3 | sed 's:^:\t:'; } 3>&1

Both fd 1 and 3 of script.sh and sed will point to the original stdout destination however. If you want to be a good citizen, you can close those fd 3 which those commands don't need:

{ ./script.sh 2>&1 >&3 3>&- | sed 's:^:\t:' 3>&-; } 3>&1

bash and ksh93 can condense the >&3 3>&- to >&3- (fd move).

2
  • The bash manual isn't entirely clear to me. I don't quite understand why smth like ./script.sh > /tmp/stdout_goes_here |& grep 'grepping_script_stderr' doesn't work as intended, ie: redirect script.sh's stdout (which, according to the manual snippet should happen first), then allow grep to process the script's stderr. Instead, stderrand tdout` both end up in stdout_goes_here Commented Nov 11, 2019 at 13:05
  • 2
    @sxc731 |& is shorthard for 2>&1 |. So >/tmp/stdout_goes_here |& redirects stdout to /tmp/stdout_goes_here, then 2>&1 redirects stderr to wherever stdout is going, which is /tmp/stdout_goes_here, and finally | doesn't receive any input because the output of the command has been redirected. Keep in mind that >&1 redirects to wherever file descriptor 1 is currently going, not to wherever file descriptor 1 will end up going. To pipe stderr only and redirect stdout to a file, one way is 2>&1 >/tmp/stdout_goes_here |. Commented Nov 11, 2019 at 23:29
8

|& pipes stderr to stdin, like 2>&1 |, so the next program will get both on stdin.

$cat test.sh
#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2
$./test.sh | sed 's:^:\t:'
Error Text.
        Normal Text.
$ ./test.sh |& sed 's:^:\t:'
        Normal Text.
        Error Text.
2
  • Oh, so it's basically equivalent to the longer expression runcommand 2>&1 | tee? ie runcommand |& tee? Commented Nov 10, 2011 at 23:52
  • yes, they're the same. Commented Nov 11, 2011 at 3:36
3

|& in bash is just a (not terribly portable) shortcut for 2>&1 |, so you should see every line indented.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.