Toward Cleaning Up Out-of-Date or Broken Examples In The GNU Bash Project
I had great fun putting together the tutorial Discovering Web Access Latencies Using Bash Co-Processing. However, speaking about examples that demonstrate the fabulous features of GNU Bash…
Bash is an important and even critical software project. Yet it’s a shame that the project does not always keep its examples and illustrations up to date or even accurate. The project should strive to showcase Bash’s latest features in an authoritative way. And it should prune any items that are no longer relevant or that don’t actually work. A case in point involves the coproc examples located in the bash-4.0/examples/functions directory. Now that there is support for rudimentary (i.e., single instance) coprocessing in Bash4, the old coproc examples should be updated with native implementations.
But wait, that’s not all. There are at least two problems with the illustration of coprocessing as currently shown in the GNU Bash project files. As seen in the coshell.README file, the synopsis for using the coproc.bash example function is:
Figure 1. Excerpt from bash-4.0/examples/functions/coshell.README (DOES NOT WORK)
...
Attached to the message you will find coprocess.bash and coshell.bash.
Here's a brief synopsis of use:
coprocess open telnet localhost
while coprocess read il ; do # ← PROBLEM 1
echo "$il"
case "$il" in
*ogin:*)
coprocess print 'user'
;;
*ord:*)
echo 'pass' |coprocess print --stdin
;;
*$ *) # ← PROBLEM 2
coprocess print 'exit'
break
;;
esac
done
coprocess close
...
The first problem with this example is that the Bash builtin read command, which coprocess read eventually calls, is by default in line mode which means that it normally won’t release characters for consumption until it receives a carriage return. Therefore, in this illustration, characters that are not released by a carriage return will not be seen by the body of the case statment and consequently the desired login and password responses will not be executed. A fix for this problem requires restructuring the program (see Figure 2 below).
The second problem with this illustration is in the case statement itself. The ‘*$ *’ pattern produces a syntax error because the space in the pattern needs to be escaped. This has been fixed in the version below.
Figure 2. A corrected version of the coprocessing excerpt from bash-4.0/examples/functions/coshell.README
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #!/bin/bash . ./coproc_orig.bash coprocess open telnet localhost # initial read mode: blocks until read sees ':' # (responds to Login and Password prompts which # are normally followed by ': ', not carriage returns) while coprocess read -d: il ; do echo "$il" case "$il" in *ogin*) coprocess print 'lizbennett' ;; *ord*) echo 'pride&prejudice' |coprocess print --stdin break ;; esac done coprocess print 'exit' # next read mode: blocks until read sees carriage return while coprocess read il ; do echo "$il" case "$il" in # just logs off at echo of first prompt *$\ exit) break ;; esac done coprocess close |
Now that we’ve fixed the example of the old approach to coprocessing, let’s put together some code to demonstrate the new way. The following is an updated — and correct
— illustration of coprocessing as supported natively in Bash4. This example uses the new coproc command. It also handles user name and password input. In this example coproc invokes telnet to remotely execute a command given as a command line argument. The program can also run a stream of commands provided on stdin. Enjoy!
Figure 3. Native Coprocessing Example Using Bash 4.0 coproc Command
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #!/bin/bash4 # Edit above line to point to where your Bash 4 executable lives # e.g., /bin/bash if you are running a distribution like Fedora 11 # that supports the latest version of the shell # # Examples: # # Save this program as test_native_coproc.sh in the current directory, # then run chmod +x test_native_coproc.sh, # and then execute commands like the following: # # ./test_native_coproc.sh date # echo -e "date\nuname -r\nwho" | ./test_native_coproc.sh -i -u darcy -p secret # ####################################################################################### # # process command line arguments OPTIND=1 while getopts "ip:u:" c; do case $c in i) USE_STDIN=y ;; p) MYPASSWORD=$OPTARG ;; u) MYUSER=$OPTARG ;; esac done shift $(( $OPTIND - 1 )) ARGS="$@" MYUSER=${MYUSER:-lizbennett} MYPASSWORD=${MYPASSWORD:-pride&prejudice} # set up telnet coprocess, log in and then execute a single command # or stream of commands if using stdin coproc telnet localhost while read -d: -u ${COPROC[0]}; do echo "$REPLY" case "$REPLY" in *ogin) echo "$MYUSER" >&${COPROC[1]} ;; *ord) echo "$MYPASSWORD" >&${COPROC[1]} break ;; esac done case $USE_STDIN in y) cat >&${COPROC[1]} ;; *) echo "$ARGS" >&${COPROC[1]} ;; esac echo "exit" >&${COPROC[1]} while read -u ${COPROC[0]}; do echo "$REPLY" done |

Copyright © 2009 Technetra. This code is covered by the MIT License. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.