Hereblock cmds stdout/stderr are sent to a single file and display. Hereblock cmds stdout/stderr are sent to separate files and stdout to display.
and >&2 means send the output to STDERR, So it will print the message as an error on the console. You can understand more about shell redirecting from those references: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Redirections.
Understanding the concept of redirections and file descriptors is very important when working on the command line. To redirect stderr and stdout , use the 2>&1 or &> constructs.
{ command1 2>&3 | command2; } 3>&1 1>&2 | command3
You can use up to 7 other file descriptors: from 3 to 9.
If you want more explanation, please ask, I can explain ;-)
{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'
output:
b2
a1
Produce two log files:
1. stderr
only
2. stderr
and stdout
{ { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log
If command
is echo "stdout"; echo "stderr" >&2
then we can test it like that:
$ { { { echo out>&3;echo err>&1;}| tee err-only.log;} 3>&1;} > err-and-stdout.log
$ head err-only.log err-and-stdout.log
==> err-only.log <==
err
==> err-and-stdout.log <==
out
err
The accepted answer results in the reversing of stdout
and stderr
. Here's a method that preserves them (since Googling on that purpose brings up this post):
{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command
Notice:
3>&-
is required to prevent fd 3 from being inherited by command
. (As this can lead to unexpected results depending on what command
does inside.)Outer part first:
3>&1
-- fd 3 for { ... }
is set to what fd 1 was (i.e. stdout
)1>&2
-- fd 1 for { ... }
is set to what fd 2 was (i.e. stderr
)| stdout_command
-- fd 1 (was stdout
) is piped through stdout_command
Inner part inherits file descriptors from the outer part:
2>&1
-- fd 2 for command
is set to what fd 1 was (i.e. stderr
as per outer part)1>&3
-- fd 1 for command
is set to what fd 3 was (i.e. stdout
as per outer part)3>&-
-- fd 3 for command
is set to nothing (i.e. closed)| stderr_command
-- fd 1 (was stderr
) is piped through stderr_command
foo() {
echo a
echo b >&2
echo c
echo d >&2
}
{ foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /'
out: a
err: b
err: d
out: c
(Order of a -> c
and b -> d
will always be indeterminate because there's no form of synchronization between stderr_command
and stdout_command
.)
Using process substitution:
command1 > >(command2) 2> >(command3)
See http://tldp.org/LDP/abs/html/process-sub.html for more info.
{ command1 | command2; } 2>&1 | command3
Caution: commnd3
will also read command2
stdout (if any).
To avoid that, you can discard commnd2
stdout:
{ command1 | command2 >/dev/null; } 2>&1 | command3
However, to keep command2
stdout (e.g. in the terminal),
then please refer to my other answer more complex.
{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'
output:
a2
b2
c2
----12
Pipe stdout as usual, but use Bash process substitution for the stderr redirection:
some_command 2> >(command of stderr) | command of stdout
Header: #!/bin/bash
I like the answer posted by @antak, but it doesn't work correctly in zsh due to multios. Here is a small tweak to use it in zsh:
{ unsetopt multios; command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command
To use, replace command
with the command you want to run, and replace stderr_command
and stdout_command
with your desired pipelines. For example, the command ls / /foo
will produce both stdout output and stderr output, so we can use it as a test case. To save the stdout to a file called stdout and the stderr to a file called stderr, you can do this:
{ unsetopt multios; ls / /foo 2>&1 1>&3 3>&- | cat >stderr; } 3>&1 1>&2 | cat >stdout
See @antak's original answer for full explanation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With