Bash – Why ‘read -t 0’ Does Not See Input

bashread

My script (should) acts differently, depending on the presence of the data in the input stream. So I can invoke it like this:

$ my-script.sh

or:

$ my-script.sh <<-MARK
    Data comes...
    ...data goes.
MARK

or:

$ some-command | my-script.sh

where two last cases should read the data, while first case should notice the data is missing and act accordingly.

The crucial part (excerpt) of the script is:

#!/bin/bash
local myData;
read -d '' -t 0 myData;

[ -z "${myData}" ] && {
    # Notice the lack of the data.
} || {
    # Process the data.
}

I use read to read input data, then option -d '' to read multiple lines, as this is expected, and the -t 0 to set timeout to zero. Why the timeout? According to help read (typing left unchanged; bold is mine):

-t timeout time out and return failure if a complete line of input is
not read withint TIMEOUT seconds. The value of the TMOUT
variable is the default timeout. TIMEOUT may be a
fractional number. If TIMEOUT is 0, read returns success only
if input is available on the specified file descriptor
. The
exit status is greater than 128 if the timeout is exceeded

So I in case 2 and 3 it should read the data immediately, as I understand it. Unfortunately it doesn't. As -t can take fractional values (according to above man page), changing read line to:

read -d '' -t 0.01 myData;

actually reads the data when data is present and skips it (after 10ms timeout) if it is not. But it should also work when TIMEOUT is set to real 0.

Why it actually doesn't? How can this be fixed? And is there, perhaps, alternative solution to the problem of "act differently depending on the presence of the data"?

UPDATE

Thanks to @Isaac I found a misleading discrepancy between quoted on-line version and my local one (normally I do not have locale set to en_US, so help read gave me translation which I couldn't paste here, and looking up for on-line translation was faster than setting new env—but that caused the whole problem).

So for 4.4.12 version of Bash it says:

If TIMEOUT is 0, read returns
immediately, without trying to read any data, returning
success only if input is available on the specified
file descriptor.

This gives a little bit different impression than "If TIMEOUT is 0, read returns success only if input is available on the specified file descriptor"—for me it implied actually an attempt to read the data.

So finally I tested this and it worked perfectly:

read -t 0 && read -d '' myData;

The meaning: see if there's anything to read and if it succeed, just read it.

So as to base question, the correct answer was provided by Isaac. And as to alternative solution I prefer the above "read && read" method.

Best Answer

No, read -t 0 will not read any data.

You are reading the wrong manual. The man read will give the manual of a program in PATH called read. That is not the manual for the builtin bash read.

To read bash man page use man bash and search for read [-ers] or simply use:

help read

Which contains this (on version 4.4):

If timeout is 0, read returns immediately, without trying to read any data.

So, no, no data will be read with -t 0.


  1. Q1

    Why it actually doesn't?

Because that is the documented way it works.

  1. Q2

    How can this be fixed?

Only if that is accepted as a bug (I doubt it will) and the bash source code is changed.

  1. Q3

    And is there, perhaps, alternative solution to the problem of "act differently depending on the presence of the data"?

Yes, actually, there is a solution. The next sentence of help read after what I quoted above reads:

returning success only if input is available on the specified file descriptor.

Which means that even if it doesn't read any data, it could be used to trigger an actual read of available data:

read -t 0 && read -d '' myData

[ "$myData" ] && echo "got input" || echo "no input was available"

That will have no delay.

Related Question