12

I have a bash script, which runs manager() function as a separate process for x-times. How is it possible to forward messages to all manager() processes from within the script?

I've read about anonymous pipes but I have no idea how to share the messages with it.. I tried doing it with named pipes, but it appears that I would have to create a separate named pipe for each process?

What is the most elegant way to do this?

Here's my code so far:

#!/bin/bash

manager () {
    while :
    do
        echo "read what has been passed to \$line"
    done
}

x=1
while [ $x -le 5 ]
do
  manager x &
  x=$(( $x + 1 ))
done


while :
do
while read line
do
  echo "What has been passed through the pipe is ${line}"
  # should pass $line to every manager process

done < $1
done

exit 0
25

The term for what you're trying to accomplish is multiplexing.

This can be accomplished fairly easily in bash, but it does require some more advanced bash features.

I created a script based on your which I think does what you're trying to accomplish. I'll explain it below.

#!/bin/bash
manager() {
  while IFS= read -r line; do
    echo "manager[$1:$BASHPID]: $line"
  done
}

fds=()
for (( i=0; i<5; i++ )); do
  exec {fd}> >(manager $i)
  fds+=( $fd )
done

while IFS= read -r line; do
  echo "master: $line"
  for fd in "${fds[@]}"; do
    printf -- '%s\n' "$line" >&$fd
  done
done

manager is a bash function which simply reads from STDIN and writes it's identifier and the line to STDOUT. We use $BASHPID instead of $$ as $$ does not get updated for subshells (which is what we will be using to launch manager.

fds is an array that will hold the file descriptors pointing to the STDIN pipes of the various managers spawned off.
Then we loop through and create 5 manager processes. I use the for (( )) syntax instead of the way you were doing it as it's cleaner. This is bash specific, but several of the things this script does are bash specific, so might as well go all the way.
 

Next we get to exec {fd}> >(manager $i). This does several more bash specific things.
The first of which is {fd}>. This grabs the next available file descriptor on or after number 10, opens a pipe with the writing side of the pipe assigned to that file descriptor, and assigns the file descriptor number to the variable $fd.

The >(manager $i) launches manager $i and basically substitutes >(manager $i) with a path to a STDIN of that process. So if manager was launched as PID 1234, >(manager $i) might get substituted with /proc/1234/fd/0 (this is OS dependent).

So assuming the next available file descriptor number is 10, and manager is launched with PID 1234, the command exec {fd}> >(manager $i) basically becomes exec 10>/proc/1234/fd/0, and bash now has file descriptor pointing to STDIN of that manager.
Then since bash puts that file descriptor number in $fd, we add that descriptor to the array fds for later usage.
 

The rest of it is pretty simple. The master reads a line from STDIN, iterates over all the file descriptors in $fds, and sends the line to that file desciptor (printf ... >&$fd).

 

The result looks like this:

$ /tmp/test.sh
hello
master: hello
manager[0:8876]: hello
manager[1:8877]: hello
manager[4:8880]: hello
manager[2:8878]: hello
manager[3:8879]: hello
world
master: world
manager[0:8876]: world
manager[1:8877]: world
manager[3:8879]: world
manager[2:8878]: world
manager[4:8880]: world

Where I typed hello and world.

  • @Patrick the is works, but you got a typo, {fd} and it should be ${fd} – c4f4t0r May 30 '14 at 8:26
  • 3
    @c4f4t0r That's not a typo – Patrick May 30 '14 at 13:04
  • @Patrick on suse 11 "bash type.bash type.bash: line 10: exec: {fd}: not found" i changed to exec ${fd} and it work like that – c4f4t0r May 30 '14 at 14:07
  • 2
    @c4f4t0r The version of bash in open suse 11 is pretty ancient (3.2). This feature was implemented in bash 4.0. – Patrick May 30 '14 at 15:10
  • Thanks for a lot of good information! A nitpick: I can understand why you would say echo -- "$line" or printf "%s\n" "$line" – but why would you need to use -- when the next argument is hard-coded (and does not begin with -)? – Scott Jul 11 '14 at 16:57
0

tee and bash:

cat foo | tee >(manager) >(manager) >(manager) >(manager) >(manager) >/dev/null

If the number of managers should be configurable or if you want the output from different managers not to be mixed:

export -f manager
cat foo | parallel --pipe --tee manager ::: {1..10}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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