Nonblocking I/O in bash scripts

Hello,

Is there a way to perform nonblocking I/O reads from standard input in a bash script?

For example, in C, you can say:

int flags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
ch = fgetc(stdin);

the 'fgetc' function will not block on standard input but will attempt to read and return ch = -1 if there was no input waiting.

Bash has the builtin "read" command, which hangs on stdin until it can read some characters (or until it times out, if you set it to read with the -t option), but there seems to be no real way to imitate the C behavior described above. Is there?

Thanks,
Neked

Is this the appropriate forum for this topic, or should I move it elsewhere?

Use stty and dd. Here is some sample code:

stty -echo -icanon time 0 min 0
while :
do
  key=$( dd bs=1 count=1 2> /dev/null; printf "." )
  key=${key%.}
  case $key in
    q) break ;;
    ?) printf "%d\n" "'$key" ;;
  esac
done
stty sane

Pretty damn cool cfajohnson.

Here is my implementation based on your stty idea and some modifications:

#!/bin/bash

if [ -t 0 ]; then
    stty -echo -icanon time 0 min 0
fi;

read line
echo "The input was, if any: " $line

if [ -t 0 ]; then
    stty sane
fi;

if that code was saved in an executable named test.sh, then you could do the following:

bash$ ./test.sh
The input was, if any: 
bash$ echo "test" | ./test.sh
The input was, if any: test

The reason I'm using the if [ -t 0 ]; statement is to make sure that standard input is coming from the terminal, not a pipe, otherwise you'll get annoying errors:

stty: standard input: Invalid argument

This inspired me to do a much simpler version of the code above, without the use of stty:

#!/bin/bash
if [ ! -t 0 ]; then
    read line
    echo $line
fi

Unfortunately none of the approaches above work unless a terminal or a pipe is present, so if you have the aformentioned test.sh on a remote machine, then:

bash$ echo "test" | ssh remote_machine ./test.sh
test
bash$ ssh -t remote_machine ./test.sh <== using -t flag to force terminal creation

bash$ ssh remote_machine ./test.sh
<== it blocks here on the read