38

Possible Duplicate:
osx/linux: pipes into two processes?

Is there a way to pipe the output from one command into the input of two other commands, running them simultaneously?

Something like this:

$ echo 'test' |(cat) |(cat)
test
test

The reason I want to do this is that I have a program which receives an FM radio signal from a USB SDR device, and outputs the audio as raw PCM data (like a .wav file but with no header.) Since the signal is not music but POCSAG pager data, I need to pipe it to a decoder program to recover the pager text. However I also want to listen to the signal so I know whether any data is coming in or not. (Otherwise I can't tell if the decoder is broken or there's just no data being broadcast.) So as well as piping the data to the pager decoder, I also need to pipe the same data to the play command.

Currently I only know how to do one - either pipe it to the decoder and read the data in silence, or pipe it to play and hear it without seeing any decoded text.

How can I pipe the same data to both commands, so I can read the text and hear the audio?

I can't use tee as it only writes the duplicated data to a file, but I need to process the data in real-time.

Community
  • 1
  • 1
Malvineous
  • 25,144
  • 16
  • 116
  • 151
  • 4
    See http://unix.stackexchange.com/questions/28503/how-can-i-send-stdout-to-multiple-commands for example – Mat Oct 28 '12 at 09:47
  • @Mat: Can you repost your answer here? That works for me and seems to be a really nice solution. – Malvineous Oct 28 '12 at 10:06
  • 1
    Yes I think this is a duplicate of that one – Malvineous Oct 28 '12 at 10:47
  • If you're just trying to save tying something multiple times in a command then the most simple solution would be `x=myoutput && command one $x && command two $x` – tumelo Dec 16 '22 at 18:44
  • @tumelo: The question is about passing the data in on standard input, but your example only places the data in a command line parameter, it doesn't pass it to the command on `stdin`. – Malvineous Dec 18 '22 at 12:39

3 Answers3

43

It should be ok if you use both tee and mkfifo.

mkfifo pipe
cat pipe | (command 1) &
echo 'test' | tee pipe | (command 2)
Tim Green
  • 3,571
  • 2
  • 23
  • 23
  • 2
    This is very useful to save IO when e.g. making backups. One can pipe the `tar` output through `pv` and through `sha512sum` before writing it, avoiding double or even triple reads/writes to/from disk. – Evi1M4chine Dec 10 '13 at 14:14
  • 2
    by the way, since this can get very confusing, if you want to fail on a certain command, you have to write `{ somecommand || errorhandler; } | tee pipe | command2`, or even `{ somecommand || errorhandlerforcommand1; } | tee pipe | command2 || errorhandlerforcommand2`, as both `somecommand || errorhandler | tee pipe | command2` and `somecommand | tee pipe | command2 || errorhandler` are not handling errors of `somecommand` as one might expect! – Evi1M4chine Dec 10 '13 at 17:25
  • 1
    Finally, to pipe just `stderr` into e.g. a log handler, you must add `2> >(errlogger)` *before* the `||` as so: `{ somecommand 2> >(errlogger) || errorhandlerforcommand1; } | tee pipe | command2`. Everything else will explode in your face. – Evi1M4chine Dec 10 '13 at 17:29
29

Recent present >(command) syntax:

echo "Hello world." | tee >(sed 's/^/1st: /')  >(sed 's/^/2nd cmd: /') >/dev/null

May return:

2nd cmd: Hello world.
1st: Hello world.

download somefile.ext, save them, compute md5sum and sha1sum:

wget -O - http://somewhere.someland/somepath/somefile.ext |
    tee somefile.ext >(md5sum >somefile.md5) | sha1sum >somefile.sha1

or

wget -O - http://somewhere.someland/somepath/somefile.ext |
    tee >(md5sum >somefile.md5) >(sha1sum >somefile.sha1) >somefile.ext

Old answer

There is a way to do that via unnamed pipe (tested under linux):

 (( echo "hello" |
         tee /dev/fd/5 |
             sed 's/^/1st occure: /' >/dev/fd/4
    ) 5>&1 |
    sed 's/^/2nd command: /'
 ) 4>&1

give:

2nd command: hello
1st occure: hello

This sample will let you download somefile.ext, save them, compute his md5sum and compute his sha1sum:

(( wget -O - http://somewhere.someland/somepath/somefile.ext |
    tee /dev/fd/5 |
    md5sum >/dev/fd/4
  ) 5>&1 |
  tee somefile.ext |
  sha1sum
) 4>&1
F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
6

Maybe take a look at tee command. What it does is simply print its input to a file, but it also prints its input to the standard output. So something like:

echo "Hello" | tee try.txt | <some_command>

Will create a file with content "Hello" AND also let "Hello" (flow through the pipeline) end up as <some_command>'s STDIN.

Stphane
  • 3,368
  • 5
  • 32
  • 47
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • 2
    *I can't use tee as it only writes the duplicated data to a file, but I need to process the data in real-time.* – cnicutar Oct 28 '12 at 09:48
  • Maybe I missunderstood you. Don't you need the raw data to be passed to play? – Ivaylo Strandjev Oct 28 '12 at 09:49
  • @izomorphius: I need to hear the data "live" - if it is redirected to a file, there will be a delay, so it will no longer be live. Also the file will grow in size until I run out of disk space, if I listen for a long time! – Malvineous Oct 28 '12 at 09:51
  • 1
    But you can use tee to redirect the output to a pipe. It does not have to be a file. – Ivaylo Strandjev Oct 28 '12 at 09:52
  • 3
    @izomorphius: Ah yes, but I had forgotten about pipes and that was conveniently missing from your answer :-P – Malvineous Oct 28 '12 at 10:48