Reputation: 81
This line writes "foo", sleeps, and then writes "bla":
echo "woo"; sleep 2; echo "bla"
I'm trying to read the entire output of this, in two sequential read commands:
(echo "woo"; sleep 2; echo "bla") 1> \
>(IFS=""; read -t 0.1 -r -N 10; \
echo "exit code: $? reply: $REPLY"; \
sleep 5; \
read -t 0.1 -r -N 10; \
echo "exit code: $? reply: $REPLY")
The first read prints:
exit code: 142 reply:
which is kinda expected, since 142 is a timeout, and I called read with -t 0.1. But the second read prints:
exit code: 1 reply: bla
Question: where did "woo" go?!
If I remove the sleep 2
from the output line, the whole thing works as expected -- reads/outputs the entire "woo\nbla" sequence in the first read, and returns 1 (EOF).
The problem reproduces with any sleep, no matter how short. It also doesn't matter if I use a pipe to redirect output instead of >1. This is Ubuntu.
Edit: I want to read into a buffer of N characters, and read the input as-is, without any word-splitting on delimeters. Hence the -N and IFS="".
Edit: this is a toy example to demonstrate a general problem. What I'm trying to do is to implement a smarter version of tee in bash: it should behave like tee, except it should wait for its input-generating process to stop, then flush its buffers, and exit. The real tee hangs indefinitely if the input-generating process starts some zombie child processes, because they then occupy the stdout handle and it never gets closed. Using tail --pid works, but unfortunately it doesn't work on Windows, and I need this to be multi-platform. I thought this could be accomplished by calling read -t -N in a loop, but apparently that doesn't work...
Upvotes: 7
Views: 847
Reputation: 3417
Question: where did "woo" go?!
The woo
was read by the first read -t 0.1 -r -N 10
and then this command failed, because it did not receive 10 characters or EOF character within the set timeout.
According to the read man page:
-N nchars return only after reading exactly NCHARS characters, unless EOF is encountered or read times out, ignoring any delimiter -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
Edit: I want to read into a buffer of N characters, and read the input as-is, without any word-splitting on delimeters. Hence the -N and IFS="".
If you call read
command two times, each instance of it has a separate buffer. To solve this use a single read
command and adjust or remove the timeout option eg:
(echo "woo"; sleep 2; echo "bla") 1> \
>(IFS=""; read -r -N 10; \
echo "exit code: $? reply: $REPLY";)
And then, if you want to simultaneously view the incoming input, you can add the tee
command, eg:
(echo "woo"; sleep 2; echo "bla") 1> \
>(tee >(IFS=""; read -r -N 10; \
echo "exit code: $? reply: $REPLY";))
Upvotes: 6
Reputation: 11469
You need to introduce a sleep
right before reading the output from stdout. This sleep
should be at least the same or larger amount that the input.
(echo "woo"; sleep 1; echo "blah") 1> >( IFS=""; \
sleep 1; read -t 0.1 -N 10; echo $REPLY; sleep 2; \
read -t 0.1 -N 10; echo $REPLY)
In your case echo woo
gets gobbled up by the following sleep command. By the time output
redirection happens, all it sees sleep 1; echo "blah"
.
And you have another problem. You are trying to read 10
characters with -N
option, where as you are providing is 3
, causing it to exit with code 142
. Try with -N 4
.
Following will exit with code = 0
;
$ (echo "woo"; sleep 1; echo "blah") 1> >( IFS=""; \
sleep 1; read -t 0.1 -N 4; echo "code: $?"; \
echo $REPLY; sleep 2; read -t 0.1 -N 4; echo "code: $?"; \
echo $REPLY)
code: 0
woo
code: 0
blah
Upvotes: 1