Using BASH to read ANY character from a file stream.
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
The "$char" variable is only valid if a character was actually read, and that only happens if its status is true (0).
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...
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"
The result output is... a-TAB-b-RETURN-c-NEWLINE-d-NULL-e perfect!
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
printf 'a\tb\rc\nd\0e' | od -c
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).
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
IFS= read -r -s -d '' -n 1 -t 1 char
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.
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.