By A.Thyssen at 2011-09-30 03:33
This comes out of a discussion...
Bash script to accept password and replace characters with * as they are typed
Reading ANY character in BASH...
The "$char" variable is only valid if a character was actually read, and that only happens if its status is true (0).
IFS= read -r -s -d '' -n 1 char
If its exit status is not true then either EOF has been reached (exit status = 1) or some error occurred (exit status > 128). That latter can happen for example if the shell script received a broken pipe, but the SIGPIPE is being ignored.
There is however a special condition... If the read returned true, but the "$char" variable is empty, then the read command actually read a NULL character. Bash can not handle NULL characters in its variables, so it sees an empty variable.
This is the important special case that needs to be handled so as to allow BASH to read any character from its input stream.
Here is an example of using it...
The result output is... a-TAB-b-RETURN-c-NEWLINE-d-NULL-e perfect!
printf 'a\tb\rc\nd\0e' |
while IFS= read -r -s -d '' -n 1 char
if [[ "$char" == '' ]]; then echo -n "-NULL-"
elif [[ "$char" == $'\r' ]]; then echo -n "-RETURN-"
elif [[ "$char" == $'\n' ]]; then echo -n "-NEWLINE-"
elif [[ "$char" == $'\t' ]]; then echo -n "-TAB-"
else echo -n "$char"
Note I used the built-in "printf" command so I can actually output a NULL. "echo" can not be used for printing NULL characters. You can check the output contains NULL's using
Both the IFS= and the " -d '' " option in the "read" command is important, otherwise read will delimit the input based on white-space (no IFS=) or NEWLINES (no -d).
printf 'a\tb\rc\nd\0e' | od -c
If the -d is not included then NEWLINE will also return an empty string (as a delimiter) in which case the result is a-TAB-b-RETURN-c-NULL-d-NULL-e
This may be desirable in some cases.
The read can be made more complicated by also allowing it to timeout
You still only get a true exit status if a character is read. but you may also get a exit status of 142 on linux machines if the read timed out.
IFS= read -r -s -d '' -n 1 -t 1 char
Poll reads (warning)...
If you use a zero timeout " -t 0 " so as to 'poll' to see if there is any input waiting, then you will not get a timeout exit status. Instead (at least on my machine) I get a exit status of 1 the same as for an EOF condition. -- This I regard as a BUG in BASH.
I have not been able to find a way to use read to 'poll' for input and differentiate between no input and a EOF condition, using pure BASH. :-(
Though one method I have found is to use a small perl script to make a IO library 'select()' system call to see if input is available before doing the "read". But I consider that cheating.
Anthony Thyssen <A.Thyssen@griffith.edu.au>