40

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
  • 4
    See unix.stackexchange.com/questions/28503/… for example
    – Mat
    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.
    – Malvineous
    Commented Oct 28, 2012 at 10:06
  • 1
    Yes I think this is a duplicate of that one
    – Malvineous
    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
    – tumelo
    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.
    – Malvineous
    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)
4
  • 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.
    – anon
    Commented Dec 10, 2013 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!
    – anon
    Commented Dec 10, 2013 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.
    – anon
    Commented Dec 10, 2013 at 17:29
  • cat pipe | should be replace by <pipe. Is the common way to make a command read input from file or pipe instead of stdin.
    – nadapez
    Commented Jan 10 at 17:31
32

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

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

or event more usefull:

url='http://ftp.uio.no/debian-cd/current/amd64/iso-cd'
read -r 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
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
  • 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
    Commented Oct 28, 2012 at 9:48
  • Maybe I missunderstood you. Don't you need the raw data to be passed to play? Commented Oct 28, 2012 at 9: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
    Commented Oct 28, 2012 at 9:51
  • 2
    But you can use tee to redirect the output to a pipe. It does not have to be a file. Commented Oct 28, 2012 at 9:52
  • 3
    @izomorphius: Ah yes, but I had forgotten about pipes and that was conveniently missing from your answer :-P
    – Malvineous
    Commented Oct 28, 2012 at 10:48

Not the answer you're looking for? Browse other questions tagged or ask your own question.