Make grep exit earlier when it finds a match

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 to do is exit sleep 30

early if he sees "baz". The -m flag exits grep, but doesn't kill the whole line.

How can I achieve this in bash (not using expect

if possible)?


Update: The above code exits if and only if the server tries to send something after baz. This does not solve this problem as the server might not send anything in a matter of minutes.

+3


source to share


4 answers


If you like the esoteric side 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 use coproc

to run



{ { sleep 1; echo "foo"; sleep 1; echo "bar"; sleep 30; } | nc localhost 2222; }

      

in the background. coproc

redirects the standard output and standard input of this compound command to the file descriptors set to ${COPROC[0]}

and ${COPROC[1]}

. Moreover, PID

this quest is located in COPROC_PID

. Then we root the grep

standard output of the background job. After that, it's easy to kill the job when we're done.

+3


source


You can catch the pid from the subshell you open. Then something like this should do:

( 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 subshell ID in a temporary file and then continue with the description.

On the other side of the pipe, you read the contents of the file ( sleep 1

must make sure that it was written to the file by the original subshell), and when you find content from 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 a bash restart.

Loans for:

+3


source


Unexpectedly 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 doesn't work. But after adding echo -n '.'

it it works. It seems that trying to write a closed pipe results in an abort. Although I tested without nc.

+1


source


I think you really need to use expect

( http://expect.sourceforge.net/ and there are packages for most OS and distributions).

Otherwise, it will be difficult for you to deal with some cases and get rid of buffering, etc. Expect this for you (... well, once you've written the right, except the script that handles all (or most) of the cases) (For the first draft, you can use autoexpect ( http://linux.die.net/man / 1 / autoexpect ), but you will need to add options (handling "wrong password" messages, etc.))

Expect this to be an old tool (and it is based, iirc, on Tcl), but there is really no better tool for specifying "sending input and waiting for outputs (and reacting differently depending on the outputs)"

+1


source







All Articles