On 10/13/25 11:08 PM, Martin D Kealey wrote:
On Thu, 9 Oct 2025 at 02:19, Pourko via Bug reports for the GNU Bourne Again SHell <[email protected]> wrote:Now, if only we had a way to say: read -d "" --noblockYeah, and it would help if « read -t0 -n0 » worked consistently with other timeout values too. «read» treating zero timeout as meaning hopefully-non-destructive look-ahead was added in the weekly snapshot on 2008-09-04 (commit:48ff544772dfa6b8fa3dda69361a5d64a9b0cbcd). Before that, a zero timeout was always an error. In hindsight I think this was a suboptimal design choice; I would have preferred it to be treated like O_NONBLOCK.
Hindsight is always perfect.
«read» treating zero size as meaning non-destructive (but less portable) look-ahead was added in the weekly snapshot on 2017-06-30 (commit:9952f68c0817d518471ed88090c79dbc761b96de). In this case 0 returned by the «read» syscall means “no error” rather than EOF, and the «read» built-in usefully returns 0 in this case.
It doesn't really read ahead, it just validates the file descriptor.
However this depends on how the «read» syscall treats a zero «count» parameter.
POSIX specifies the behavior, but if you want to call read specifying zero characters, bash will defer to the OS.
Currently, very short timeouts (less than about 30µs on my machine) always result in timing out, even when data is already available. This makes them largely indistinguishable from a zero timeout, at least on systems that can report EAGAIN from a zero-sized non-blocking read.
Bash doesn't use non-blocking reads.
I'm guessing this is because this corresponds to the (very) small delay between setting an alarm timer, and then initiating a read.
`read' timeouts dont use SIGALRM; it uses the timeout with select. However, if the timer has already expired before bash calls select, it will return an `expired' status.
In particular, a zero timeout on read should simply mean “give me what you already have, without waiting for any more to become available”; it should not mean “give up before giving me stuff that's already available, based on some implementation race condition”.
It means neither of those things. It means "tell me whether input is available."
Currently when «read» is given «-t0», both «-n» and «-d» are ignored,
They're not.
does anyone think any of the following proposed changes would break existing working scripts?
1. Explicitly define the behaviour or «read -t0 -N0» as a non-destructive lookahead, while deprecating «read -t0» without «-N0» for this purpose.
I think the current behavior of read -t0 -N1 is fine for this.
2. Alter «read -t0 -N$count» to place up «$count» bytes of input in «REPLY», but only from what is already available. In particular, when reading from a pipe, it consumes exactly the whole content of the kernel's pipe buffer (½KiB for POSIX; 4KiB or one vmpage for Linux) provided that «$count» is large enough).
The problem is that there is no portable way to determine `what is already available'. This approach might work with pipes, but it doesn't extend to other types of file descriptors.
3. Alter «read -t0 -d$'\n'» to place a line of input in «REPLY» if one is already available. If only a partial line of input is available (missing a newline), then:
You can't know this without trying to read one. You have to put the fd in non-blocking mode and read until you get the delimiter or -1/EAGAIN.
(3a) when reading from a tty that's already in ICANON (“cooked”) mode, «REPLY» is left empty without consuming any input; and (3b) in all other cases, any input that is consumed is placed in «REPLY», but the consumption is unspecified and explicitly documented as “may change”.
Are you saying the script/application would change the terminal modes itself?
4. Alter «read -t0 -n0 -d$'\n'» to perform look-ahead while remaining in ICANON mode.
This doesn't work: either you put the fd in non-blocking mode (or ~ICANON mode) or select/pselect will return failure. That's where we started with this issue.
5. The behaviour «read -t$non_zero -N$count» and «read -t$non_zero -d$'\n'» be stabilized to be consistent with (1) through (4); in particular, this means that any SIGALRM timer should not start until after a non-blocking read has reported «EAGAIN».
Timeouts don't use SIGALRM or non-blocking reads.
6. The behaviour of «-d» with something other than $'\n' be made as consistent as possible with (1) through (5) (As an implementation detail, when reading from a tty, it would be nice to use «termios.c_cc[VEOL]» for «-d» on systems that support it.)
kre brought this up earlier; I haven't looked at it because the existing
code path uses single-byte reads.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU [email protected] http://tiswww.cwru.edu/~chet/
OpenPGP_signature.asc
Description: OpenPGP digital signature
