ERIK_SON ERIK_SON - 2 months ago 12
Bash Question

Shell POSIX two nested while read and read from stdin not working

I have that sample script:

#!/bin/sh
while read ll </dev/fd/4; do
echo "1 "$ll

while read line; do
echo $line
read input </dev/fd/3
echo "$input"
done 3<&0 <notify-finished

done 4<output_file


Currently The first loop do not iterate just stays on line 1. How do I fix that without bashisms because it has to be highly portable. Thanks.

Answer

Your code already has bashisms. Here, I'm taking them out (and simplifying the FD handling for better readability):

#!/bin/sh
while read ll <&4; do            # read from output_file
     printf '%s\n' "1 $ll"

     while read line <&3; do     # read from notify-finished
          printf '%s\n' "$line"
          read input             # read from stdin
          printf '%s\n' "$input"
     done 3<notify-finished

done 4<output_file

Run the script as follows:

echo "output_file" >output_file
echo "notify-finished" >notify-finished
echo "stdout" | ./yourscript

...and it correctly exits with the following output:

1 output_file
notify-finished
stdout

Notes:

  • echo's behavior is wildly nonportable across POSIX platforms. See the APPLICATION USAGE section of the POSIX spec for echo, which advises using printf instead.
  • /dev/fd/## is not specified by POSIX; it is an extension made available both by Linux distributions (creating a symlink to /proc/self/fd -- /proc being itself an unspecified extension) and by bash itself. Use <&4 in place of </dev/fd/4.
  • You probably want to use the -r argument to read -- which is POSIX-specified, and prevents the default behavior of treating backslashes as escape sequences for newlines and characters in IFS. Without it, foo\bar is read as foobar, thus not reading your data as it truly exists in its input sources.
Comments