47

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)
user14492
  • 753
  • 1
  • 8
  • 15

3 Answers3

47

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)
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • This is good, also a good solution. – Merlin Jan 06 '20 at 16:43
  • 1
    ~~Does anyone know what this redirection look like in fish shell?~~ The [docs](https://fishshell.com/docs/current/fish_for_bash_users.html?highlight=redirect#process-substitution) say _'There is no equivalent to `>(command)`.'_ – user14492 Jun 30 '21 at 00:17
  • This doesn't work in [Makefile](https://unix.stackexchange.com/a/356539/10451). Any other way to do it without process substitution? – Kamil Dziedzic Apr 06 '23 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. – Gilles 'SO- stop being evil' Apr 06 '23 at 19:40
28

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
nullstd
  • 13
  • 3
cuonglm
  • 150,973
  • 38
  • 327
  • 406
  • 3
    `tee $(tty)` Or, y'know, `tee /dev/tty` – Kenster Mar 30 '16 at 12:59
  • great explanation, top SO answer all around. – Merlin Jan 06 '20 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 '20 at 01: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 '20 at 07: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.

cuonglm
  • 150,973
  • 38
  • 327
  • 406
Archemar
  • 31,183
  • 18
  • 69
  • 104
  • 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 '21 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 '21 at 08:15