DEV Community

Cover image for Bidirectional piping, how to make two programs communicate with each other
Marcell Cruz
Marcell Cruz

Posted on • Updated on

Bidirectional piping, how to make two programs communicate with each other

First of all a small refresher on what piping is and how it works
when you do something like

echo 2 | cat
Enter fullscreen mode Exit fullscreen mode

stdout of echo is being piped to cat's stdin, cat writes again the result that was read from stdin to stdout and you see the result in your terminal, this is a example of unidirectional piping, bidirectional piping would be if cat could write back to echo and echo could read what was piped, basically you would have two program that can communicate with each other.

On Linux we do this creating a named pipe, which is just a special type of file using the FIFO(first in first out) method so one program can write to that file and the other can read and vice versa, the two easiest ways of creating a FIFO is using the program mkfifo, or using bash built-in function coproc, both do basically the same thing the only different is that when using coproc you end up with more elegant code depending on who you ask, let's create a simple program and implement bidirectional piping using mkfifo, which is more explicit, I'm gonna write one program in bash and the other one in javascript(node) but any language that can write to stdin, stderr and read from stdout would work(so basically all of them)
let's create three programs, prog1, prog2 and program to create the FIFO and call the other programs

prog1.sh

echo "bash: hello!"
read line
echo $line 1>&2
echo "bash: how are you doing?"
read line
echo $line 1>&2
Enter fullscreen mode Exit fullscreen mode

prog2.js

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', function (cmd) {
  if(cmd == 'bash: hello!') {
    console.error(cmd);
    console.log("javascript: hello!");
  }
  if(cmd == 'bash: how are you doing?') {
    console.error(cmd);
    console.log("javascript: I'm fine, thanks!");
  }
});
Enter fullscreen mode Exit fullscreen mode

and the program to create the FIFO and call the two other programs

start.sh

mkfifo p1 p2 
bash ./prog1.sh > p1 < p2 &
node ./prog2.js < p1 > p2 

rm -rf p1 p2
Enter fullscreen mode Exit fullscreen mode

We delete the FIFOs in the last line to avoid a warning the next time we run bash start.sh, the warning would just tell us that the FIFO already exist.
now we can run everything with

bash ./start.sh
Enter fullscreen mode Exit fullscreen mode

Image description

It works!!! one program can send messages to the other program, when both programs end, start also ends, also we can only read or write to the FIFO if two program open the FIFO file, if you remove the last line of start.sh you can see the FIFO files that were created in the same directory.

Image description

If you try to open the FIFO file in a text editor, your text editor will probably crash on hang indefinitely. you might think that the same thing is possible just creating regular files, but the code would be a lot more cumbersome, FIFO allows us to write pretty elegant code connecting two different programs, or two different instances of the same program, let's talk a little bit about the actual code

mkfifo p1 p2
Enter fullscreen mode Exit fullscreen mode

This command creates two FIFO files called p1 and p2

bash ./prog1.sh > p1 < p2 &
Enter fullscreen mode Exit fullscreen mode

here we run the first bash program and tell it to point it's stdout to p1 and stdin to p2

node ./prog2.js < p1 > p2
Enter fullscreen mode Exit fullscreen mode

here we run the second program and do the opposite, we tell him to read from p1 and write to p2

rm -rf p1 p2
Enter fullscreen mode Exit fullscreen mode

This line is not required it just avoids the warning when we run mkfifo again the next time

Now let's have a look at the programs themselves, first the bash program

echo "bash: hello!"
read line
echo $line 1>&2
echo "bash: how are you doing?"
read line
echo $line 1>&2
Enter fullscreen mode Exit fullscreen mode

The only think that might be confusing here if you don't know bash very well is the line

echo $line 1>&2
Enter fullscreen mode Exit fullscreen mode

here we're redirecting from stdout to stderr if we don't do this we can't see the messages in our terminal, remember that the stdout is going to the other program and not to the terminal, if we just echoed without the redirecting the string will go to the FIFO and be read by prog2, same thing for the javascript code

console.error(cmd);
Enter fullscreen mode Exit fullscreen mode

we do the above so we can print to stderr, the javascript program prints the bash messages and the bash program prints the javascript messages, another difference is that the javascript program calls the same callback every time it reads something from the fifo that's why we have the if statements to identify which message was receive and reply accordingly.

Discussion (0)