53

Is there a way to pipe the output of a command and direct it to the stdout as well?

So for example, fortune prints a fortune cookie to stdout and also pipes it to next command:

$ fortune | tee >(?stdout?) | pbcopy 
"...Unix, MS-DOS, and Windows NT (also known as the Good, the Bad, and
the Ugly)."
(By Matt Welsh)
2

3 Answers 3

50

tee always writes to its standard output. If you want to send the data to a command in addition to the terminal where the standard output is already going, just use process substitution with that command. (Note that in spite of starting with >, process substitution does not redirect standard output, the tee command sees it as a parameter.)

fortune | tee >(pbcopy)
4
  • This is good, also a good solution.
    – Merlin
    Jan 6, 2020 at 16:43
  • 1
    ~~Does anyone know what this redirection look like in fish shell?~~ The docs say 'There is no equivalent to >(command).'
    – user14492
    Jun 30, 2021 at 0:17
  • This doesn't work in Makefile. Any other way to do it without process substitution? Apr 6 at 18:18
  • @KamilDziedzic In a makefile, duplicating output is rarely a good idea in the first place, because rules with multiple outputs are poorly supported. Write the output to a file, and use separate rules for each way you want to post-process it. Apr 6 at 19:40
30

Your assumption:

fortune | tee >(?stdout?) | pbcopy

won't work because the fortune output will be written to standard out twice, so you will double the output to pbcopy.

In OSX (and other systems support /dev/std{out,err,in}), you can check it:

$ echo 1 | tee /dev/stdout | sed 's/1/2/'
2
2

output 2 twice instead of 1 and 2. tee outputs twice to stdout, and tee process's stdout is redirected to sed by the pipe, so all these outputs run through sed and you see double 2 here.

You must use other file descriptors, example standard error through /dev/stderr:

$ echo 1 | tee /dev/stderr | sed 's/1/2/'
1
2

or use tty to get the connected pseudo terminal:

$ echo 1 | tee "$(tty)" | sed 's/1/2/'
1
2

With zsh and multios option set, you don't need tee at all:

$ echo 1 >/dev/stderr | sed 's/1/2/'
1
2
4
  • 3
    tee $(tty) Or, y'know, tee /dev/tty
    – Kenster
    Mar 30, 2016 at 12:59
  • great explanation, top SO answer all around.
    – Merlin
    Jan 6, 2020 at 16:44
  • How come when you do ... tee /dev/stdout | sed... it returns 2 2 and not 1 2; doesn't tee run before sed? It makes no sense, especially since rest print what is expected at that point in pipe. Why is there a wormhole in the pipe? It messing me up man!
    – user14492
    Jul 19, 2020 at 1:55
  • @user14492 not sure what you mean. ... tee /dev/stdout causes result to be written to standard out twice, that's why you see 2 2.
    – cuonglm
    Jul 20, 2020 at 7:00
1

cuonglm said it all.

Just try:

fortune | tee "$(tty)" | pbcopy

tty should resolve to actual pseudo terminal (like /dev/pts/99) in interactive session (i.e. in terminal), or no a tty in batch, at and daemon.

2
  • I'm not sure why, but echo "$(tty)" → /dev/pts/2, seq 2 | tee "/dev/pts/2" | wc -l → 1 2 2, but seq 2 | tee "$(tty)" | wc -l → 2
    – Pablo A
    Aug 16, 2021 at 21:01
  • 1
    @PabloA yes "$(tty)" (quoted or not) resolv to not a tty. I am not sure how I tested it 5 years ago.
    – Archemar
    Aug 18, 2021 at 8:15

You must log in to answer this question.

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