Opening a named pipe in bash, without reading or writing to it
bash, linux, named-pipes, ruby, shell
Solution
It's pretty simple with Bash:
# Open the named pipe with a custom fd for input
exec 4< /path/to/named_pipe
# Do other things
:
# Read a line from pipe
read -ru 4 line
# Or
read -r line <&4
# Just make this consistent and close it when you're done. May not be necessary at exit but still a good practice.
exec 4<&-
- When reading input with `read`, it may be a good idea to use `-r` to prevent backslashes from escaping any character
- Running `read` with `IFS` unset (`IFS= read ...`) may also be helpful to prevent trimming of leading and trailing spaces.
Similar concept would apply for writing:
exec 4> /path/to/named_pipe
echo something >&4
exec 4>&-
Bonus: You may also open a named pipe in the scope of a block like `{}`, `while`, `for`, `if`, `until`, etc.. With it won't be necessary to close the file descriptor:
{
# Do reads and other things
...
} 4< /path/to/named_pipe
Problem
I want to use a named pipe in bash without reading or writing to it, until an event occurs. i need to keep this pipe open for the entire duration the shell script runs. I will read from it whenever I need to. Meaning, can something be done like this: ``` open(<$NAMED_PIPE>, "r+") --do something-- --call child ruby script, which will write to named pipe-- --do something more--- read line < $NAMED_PIPE ``` Is this possible in bash? I am using the following code: ../Ruby |-- try1.rb `-- try2.sh try1.rb is as follows: ``` #!/usr/bin/ruby require 'optparse' puts 'Hello World!, This is my first ruby program' options = {} optparse = OptionParser.new do|opts| opts.banner = "Featbuild minimal trial script for command line parsing" options[:cpl] = nil opts.on('--cpl SWITCH_STATE', 'compile on or off') do|cplopt| options[:cpl] = cplopt OPT_CPL=cplopt puts cplopt end opts.on('-h', '--help', 'Display this screen') do puts opts exit end end optparse.parse! output = open("mypipe", "w+") output.puts OPT_CPL output.flush ``` And the try2.sh script(which calls the ruby script): ``` #!/bin/sh mkfifo mypipe exec 4< /home/yusufh/Ruby/mypipe OPT=$* ./try1.rb ${OPT} #read -ru 4 line read line <&4 echo $line exec 4<&- #close FIFO my_pipe rm mypipe ``` @konsolebox, your solution worked when I used a normal text file where I copy the data from the ruby script, and from there itself the bash script picks it up. However, On trying with named pipes, the bash script blocks after executing the following statement:`exec 4< /home/yusufh/Ruby/mypipe`. Control doesnt reach to spawning the ruby script process, where the data will be genrated, and after that the actual waiting/blocking by the bash script has to be done at this line: `read line <&4`. Due to this, what I want to achieve isn't happening. Additional, when bash waits in when it acquires the fd of the FIFO, if I echo something into the named pipe on a duplicate terminal, it then does come out of wait and proceeds further. But then in this case, the data returned from the Ruby script is also not written to the FIFO, as I have observed. This is the output of the scritps in debug mode: ``` [yusufh@verigybuild6 Ruby]$ bash -xv try2.sh --cpl on & [1] 2777 [yusufh@verigybuild6 Ruby]$ #!/bin/sh mkfifo mypipe + mkfifo mypipe exec 4< /home/yusufh/Ruby/mypipe + exec [yusufh@verigybuild6 Ruby]$ echo "Hello World" > mypipe [yusufh@verigybuild6 Ruby]$ OPT=$* + OPT='--cpl on' ./try1.rb ${OPT} + ./try1.rb --cpl on Hello World!, This is my first ruby program on #read -ru 4 line read line <&4 + read line echo $line + echo Hello World Hello World exec 4<&- #close FIFO my_pipe + exec rm mypipe + rm mypipe [1]+ Done bash -xv try2.sh --cpl on [yusufh@verigybuild6 Ruby]$ ``` Any ideas as why this is failing?. I appreciate the efforts! :-)