Pipe output to two different commands [duplicate]
Asked Answered
R

3

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.

Rounded answered 28/10, 2012 at 9:45 Comment(5)
See unix.stackexchange.com/questions/28503/… for exampleOppugnant
@Mat: Can you repost your answer here? That works for me and seems to be a really nice solution.Rounded
Yes I think this is a duplicate of that oneRounded
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 $xNomography
@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.Rounded
S
43

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

mkfifo pipe
cat pipe | (command 1) &
echo 'test' | tee pipe | (command 2)
Spoliate answered 28/10, 2012 at 9:51 Comment(4)
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.Christianachristiane
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!Christianachristiane
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.Christianachristiane
cat pipe | should be replace by <pipe. Is the common way to make a command read input from file or pipe instead of stdin.Alberthaalberti
H
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
Headship answered 28/10, 2012 at 10:48 Comment(6)
Read my more detailled answer on this duplicateHeadship
Or my parallel.sh Parallelize stream processing using bashHeadship
If this multiple- >() syntax works for tee, why can't it work even without the tee?Fetus
@einsupportsModeratorStrike This syntax is bashism and work with any command! The purpose of tee is to multiplicate output to many targets.Headship
But why do we need tee for that, if we can use >() ? i.e. why not just echo "Hello world." >(sed 's/^/1st: /') >(sed 's/^/2nd cmd: /') ?Fetus
@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.Headship
E
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.

Echinoderm answered 28/10, 2012 at 9:46 Comment(5)
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.Impotence
Maybe I missunderstood you. Don't you need the raw data to be passed to play?Echinoderm
@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!Rounded
But you can use tee to redirect the output to a pipe. It does not have to be a file.Echinoderm
@izomorphius: Ah yes, but I had forgotten about pipes and that was conveniently missing from your answer :-PRounded

© 2022 - 2024 — McMap. All rights reserved.