Reputation: 44827
I have the following line in bash.
(sleep 1 ; echo "foo" ; sleep 1 ; echo "bar" ; sleep 30) | nc localhost 2222 \
| grep -m1 "baz"
This prints "baz" (if/when the other end of the TCP connection sends it) and exits after 32 seconds.
What I want it to do is to exit the sleep 30
early if it sees "baz". The -m flag exits grep, but does not kill the whole line.
How could I achieve this in bash (without using expect
if possible)?
Update: the code above does quit, if and only if, the server tries to send something after baz. This does not solve this problem, as the server may not send anything for minutes.
Upvotes: 5
Views: 1449
Reputation: 3801
I believe you really need to use expect
( http://expect.sourceforge.net/ , and there are packages for most OSes and distributions ).
Otherwise you'll have a hard time handling some cases and getting rid of buffering, etc. Expect does it for you (... well, once you wrote the right except script that handles all (or most) cases) (For a first draft, you can use autoexpect (http://linux.die.net/man/1/autoexpect) but you'll need to add variations (handling "wrong password" messages, etc))
Expect is an old tool (and is based, iirc, on Tcl), but there is not really a best tool for the job of "sending input and waiting for outputs (and reacting differently depending on outputs)"
Upvotes: 1
Reputation: 46903
If you like esoteric sides of Bash, you can use coproc
for that.
coproc { { sleep 1; echo "foo"; sleep 1; echo "bar"; sleep 30; } | nc localhost 2222; }
grep -m1 baz <&${COPROC[0]}
[[ $COPROC_PID ]] && kill $COPROC_PID
Here, we're using coproc
to run
{ { sleep 1; echo "foo"; sleep 1; echo "bar"; sleep 30; } | nc localhost 2222; }
in the background. coproc
takes care to redirect the standard output and standard input of this compound command in the file descriptors set in ${COPROC[0]}
and ${COPROC[1]}
. Moreover, the PID
of this job is in COPROC_PID
. We then feed grep
with the standard output of the background job. It's then easy to kill the job when we're done.
Upvotes: 4
Reputation: 2567
Suddenly found a solution based on Jidder`s comment.
(sleep 1 ; echo "foo" ; sleep 1 ; echo "bar" ; for i in `seq 1 30`; do echo -n '.'; sleep 1; done) | grep -m1 "bar"
Just sleeping in a loop does not work. But after adding echo -n '.'
it works. It seems that an attempt to write to a closed pipe leads to abort. Though I have tested without nc.
Upvotes: 1
Reputation: 290515
You can catch the pid of the subshell you are opening. Then, something like this should make:
( echo "start"; sleep 1; echo $BASHPID > /tmp/subpid; echo "hello"; sleep 20; ) \
| ( sleep 1; subpid=$(cat /tmp/subpid); grep -m1 hello && kill $subpid )
That is, you store the id of the subshell in a temp file and then continue with the descripting.
On the other side of the pipe, you read the content of the file (sleep 1
is to make sure it has been written in the file by the initial subshell) and, when you find the content with grep
, you kill it.
From man bash
:
BASHPID
Expands to the process ID of the current bash process. This differs from $$ under certain circumstances, such as subshells that do not require bash to be re-initialized.
Credits to:
Upvotes: 3