#!/bin/bash # # A interactive "bc" calculator with 8 decimal places and error handling. # # It is an example of setting up a co-process with completely separate handling # of stdin, stdout, and stderr channels. Basically this example completely # wrappers the "bc" program allowing for full program control without lockups. # # This requires an small perl program "shell_select" to allow the shell script # to access the select() library function. You can download it from... # https://antofthy.gitlab.io/software/#shell_select # # Try it with this example input. # a = 3 # 10 / a # some error # print "some output\n" # #### # # NOTE: Without the '-t 0.0001' in the shell "read" builtins below, # a print without a newline will lockup the program! In that event # you will not get a complete line from the read! # # Anthony Thyssen # mkfifo /tmp/bc_in.$$ /tmp/bc_out.$$ /tmp/bc_err.$$ bc /tmp/bc_out.$$ 2>/tmp/bc_err.$$ & bc_pid=$! bc_in=10 bc_out=11 bc_err=12 eval "exec $bc_in>/tmp/bc_in.$$" eval "exec $bc_out " user_input; do # read request from user # send user input to "bc" with -- assuming it is always okay to send echo >&$bc_in "scale=8; $user_input" # wait for any output, or timeout, -- sets $rd_ready eval `shell_select -t 1 -r $bc_out,$bc_err` # wait/timeout #echo "rd_ready = $rd_ready" if kill -0 $bc_pid 2>/dev/null; then # has co-processor exited? : all is fine else wait $bc_pid status=$? echo "BC EXIT" exit $status fi if [ -z "$rd_ready" ]; then # any output ready to read? echo >&2 "TIMEOUT: no results for expression" continue fi # handle results (stdout) or errors (stderr) from appropriate source while true; do case ",$rd_ready," in *,$bc_out,*) read -r -t 0.0001 -u $bc_out result echo "Result => $result" ;; *,$bc_err,*) read -r -t 0.0001 -u $bc_err error echo "ERROR => $error" ;; *) break ;; # no valid file descriptor - end of output esac eval `shell_select -t 0 -r $bc_out,$bc_err` # poll for more output #echo "rd_ready = $rd_ready" done done echo "NO MORE USER INPUT" echo >&$bc_in "quit" wait $bc_pid exit "$?"