[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
read -t loses data
From: |
bcboy |
Subject: |
read -t loses data |
Date: |
Sat, 30 Jul 2005 04:54:22 -0700 (PDT) |
Configuration Information [Automatically generated, do not change]:
Machine: i386
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='i386'
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='i386-pc-linux-gnu'
-DCONF_VENDOR='pc' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash' -DSHELL
-DHAVE_CONFIG_H -I. -I../bash -I../bash/include -I../bash/lib -g -O2
uname output: Linux bugs 2.6.10-5-k7 #1 Tue Apr 5 12:56:05 UTC 2005 i686
GNU/Linux
Machine Type: i386-pc-linux-gnu
Bash Version: 3.0
Patch Level: 16
Release Status: release
Description:
If "read -t" times out without fulfilling a request, any pending data is
lost.
Via strace it's apparent that the problem is that when the user requests a line
of
data, bash read is actually calling read() with a buffer size of one. So bash
is
holding the data. It's not in the OS file buffer. Then when the timeout occurs,
bash does not return the read data to the user, or associate it with the file
descriptor.
This happens when doing any multi-character read (line, delimeter, or -n
greater
than one). The only work-around is -n 1, which is painfully slow.
Repeat-By:
This is easiest to see when reading from a pipe. The "coproc.bash" example
code in
the distribution shows it quite easily. Spawn a telnet coprocess, and do a read
loop
something like
while coprocess read -t 1 line; do
echo "received (${line})"
done
Now, if you write commands to the coprocess, then invoke the read loop, you'll
find that
the command prompts from the telnet session are sometimes lost. This is because
there's no
carriage return after the prompt. Then read times out, and bash throws away the
data that
was read.
You can also do it with something like the following
function readloop()
{
while read -t 1 line <&62; do
echo "line (${line})"
done
}
fifo=/var/tmp/cot
rm -f $fifo
mkfifo $fifo
{ echo -en "This is a\ncomplete"; sleep 4; echo " sentence."; } >$fifo &
exec 62<$fifo
readloop
readloop
readloop
readloop
readloop
Fix:
The read function should either store the read data with the file
descriptor, and
prepend it to any later data read from the same file descriptor, or it should
return
the read data to the user when the read times out. If the data is stored there
could
be complications with duped file descriptors. I dunno about that. Returning the
data to
the user seems like the less error-prone thing to do, though in that case it's
important that the "name" parameters all be set to null if no data is read.
Otherwise it
is ambiguous on timeout whether the same string was read, or no data was read.
Returning
the data to the user also seems the most flexible for the programmer, since the
programmer
can then decide whether to prepend the data to the next buffer, or take
advantage of
having all of the available data from the file descriptor w/o needing to use -n
1 (one
character read loops in shell script really should be avoided).
- read -t loses data,
bcboy <=