20

I have a probably pretty easy beginner question: How do I echo from a shell script into both stdout and stderr? I know that I can echo to stderr echo "foo" 1>&2 but I need the output in both. I tried some Googling but nothing worked.

Community
  • 1
  • 1
NumberFour
  • 3,551
  • 8
  • 48
  • 72
  • possible duplicate of [Redirecting bash stdout/stderr to two places?](http://stackoverflow.com/questions/670784/redirecting-bash-stdout-stderr-to-two-places). Not exactly the same, but the answer still works if you just make your process `cat >&2` – Nemo Jul 28 '11 at 01:08

4 Answers4

29

This should do it

 echo "foo" | tee /dev/stderr 
Soren
  • 14,402
  • 4
  • 41
  • 67
  • 3
    That assumes your systems supports `/dev/stderr`. I think most or all modern Unix-like systems do, but just in case you're concerned about ancient systems: `line="foo"; echo "$line" ; echo "$line" 1>&2`. It should be easy enough to wrap that in an alias or function. – Keith Thompson Jul 29 '11 at 16:26
  • 2
    Note: you don't need `1` in `1>&2`, `echo $line >&2` does the same. – TWiStErRob Feb 27 '14 at 13:49
  • 1
    FYI - busybox's' init doesn't seem to support `/dev/stderr`. – ACK_stoverflow Jul 15 '20 at 18:36
16
echo foo | tee >(cat >&2)

This will work even if stderr is not supported.

Note: Soren's answer will not work if you have su'ed to another account that does have write permissions to the tty; but mine will.

gle
  • 211
  • 2
  • 2
  • 2
    Doesn't it require bash? Simple Bourne shell outputs `sh: Syntax error: "(" unexpected (expecting word)` – zezollo Apr 03 '21 at 13:47
0

Another solution, which might be specific to the Linux kernel (and its /proc/ filesystem), but does not require bash:

echo foo | tee /proc/$$/fd/2

Here, $$ contains the PID of the running process, and /proc/some_pid/fd/2 will point to the file descriptor number 2 of the process some_pid. We're just instructing tee to write directly to the file descriptor of the standard error output (numbered 2) of our shell process identified by its PID $$.

Tee also outputs to its standard output, which is not redirected and ends up on stdout (fd 1).

MayeulC
  • 1,628
  • 17
  • 24
0

I also second the comment on the question: as indicated by the linked answer, it is possible to make a fifo that will point to our stderr file descriptor, and then use tee to write to this:

mkfifo /tmp/temp_fifo_file # this named fifo will appear in the /tmp directory
(cat /tmp/temp_fifo_file >&2 &) # Background `cat` process: read the fifo and echo to stderr
echo foo | tee /tmp/temp_fifo_file
rm /tmp/temp_fifo_file # cleanup

Two notes on the above: cat will close the fifo and exit when EOF is received, so it only works once. I used parenthesis (subshell) to hide job control messages, but it works without.

To avoid closing the pipe, it can be opened read-write (make sure to read the linked caveat, it may be better to put the exec in the same subshell as cat, but that makes it harder to close the file descriptor):

mkfifo /tmp/temp_fifo_file # this named fifo will appear in the /tmp directory
exec 3<>/tmp/temp_fifo_file # Open fifo as fd 3
(cat 0<&3 1>&2 & ) # stays in the background, cat reads fd 3 and outputs to stderr
echo foo | tee /tmp/temp_fifo_file
# Cleanup:
exec 3<&- # Close fd 3, this will terminate background job
rm /tmp/temp_fifo_file # remove fifo

Note: 0 and 1 can be omitted in (cat 0<&3 1>&2 & ) as they designate the default (standard) input and outputs, respectively.

MayeulC
  • 1,628
  • 17
  • 24