41

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.

5
  • 5
    See unix.stackexchange.com/questions/28503/… for example Commented Oct 28, 2012 at 9:47
  • @Mat: Can you repost your answer here? That works for me and seems to be a really nice solution. Commented Oct 28, 2012 at 10:06
  • 1
    Yes I think this is a duplicate of that one Commented Oct 28, 2012 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 Commented Dec 16, 2022 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. Commented Dec 18, 2022 at 12:39

3 Answers 3

43

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

mkfifo pipe
cat pipe | (command 1) &
echo 'test' | tee pipe | (command 2)
Sign up to request clarification or add additional context in comments.

4 Comments

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.
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!
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.
cat pipe | should be replace by <pipe. Is the common way to make a command read input from file or pipe instead of stdin.
34

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, not only!

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

 (( 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

as well:

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

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 somefile.ext /dev/fd/5 |
    md5sum >/dev/fd/4
  ) 5>&1 |
  sha1sum
) 4>&1

or

wget -O - http://somewhere.someland/somepath/somefile.ext |
  (( tee /dev/fd/5 somefile.ext | md5sum >/dev/fd/4 ) 5>&1 | sha1sum ) 4>&1

More usefull sample: downloading AND checking last debian installer

url='http://ftp.uio.no/debian-cd/current/amd64/iso-cd'
file=$(
    wget -qO - "$url" |
        sed -ne 's/.*href="\(debian-[1-9]\+\..*\.iso\)".*/\1/p')
wget -O - "$url/$file" | (
    ( tee /dev/fd/5 "$file" |
        sha256sum |
            sed -ne "h;s/-/$file/;w ${file%.*}.sha256" \
                 -e 'x;s/-/SHA256/;w /dev/fd/4'
    ) 5>&1 |
        sha512sum |
            sed -e "h;s/-/$file/;w ${file%.*}.sha512" \
                -e 'x;s/-/SHA512/'
) 4>&1

Will produce 3 files and output two lines:

64d727dd5785ae5fcfd3ae8ffbede5f40cca96f1580aaa2820e8b99dae989d94  SHA256
0262488ce2cec6d95a6c9002cfba8b81ac0d1c29fe7993aa5af30f81cecad3eb66558b9d8689a86b57bf12b8cbeab1e11d128a53356b288d48e339bb003dace5  SHA512

Which could be compared with:

for i in 256 512;do wget -qO - "$url/SHA${i}SUMS" | grep $file;done
64d727dd5785ae5fcfd3ae8ffbede5f40cca96f1580aaa2820e8b99dae989d94  debian-12.4.0-amd64-netinst.iso
0262488ce2cec6d95a6c9002cfba8b81ac0d1c29fe7993aa5af30f81cecad3eb66558b9d8689a86b57bf12b8cbeab1e11d128a53356b288d48e339bb003dace5  debian-12.4.0-amd64-netinst.iso

6 Comments

If this multiple- >() syntax works for tee, why can't it work even without the tee?
@einsupportsModeratorStrike This syntax is bashism and work with any command! The purpose of tee is to multiplicate output to many targets.
@einsupportsModeratorStrike Because echo use your STDOUT for output, which is unique! You can't redirect any OUTPUT more than once! tee is intended to multiply output to as many target you address, plus STDOUT.
|
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.

5 Comments

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.
Maybe I missunderstood you. Don't you need the raw data to be passed to play?
@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!
But you can use tee to redirect the output to a pipe. It does not have to be a file.
@izomorphius: Ah yes, but I had forgotten about pipes and that was conveniently missing from your answer :-P

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.