bug-bash
[Top][All Lists]
Advanced

[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).




reply via email to

[Prev in Thread] Current Thread [Next in Thread]