12

I have a bash function like so:

run_mongo(){
  mongo foo bar baz 2>&1  # send stderr to stdout
}

unfortunately this mongo command doesn't exit with 1 if there is error, so I have to match on stdout/stderr

is there some way to exit with code > 0 with grep if there is a first match? something like this:

run_mongo | grep -e "if stdout/stderr matches this we exit with 1"

I assume the way to do this would be like so:

run_mongo | grep -e "if stdout/stderr matches" | killer

where killer is a program that dies as soon as it gets its first bit of stdin.

1
  • If the mongo command exits successfully when there was an error, and there is no option to make it do otherwise, this is a bug. Please report it. That's not even a difficult bug to fix, and then developers won't have to hack around it like you're doing.
    – Wildcard
    Commented Mar 27, 2018 at 2:59

4 Answers 4

15

Yes, you can do it with grep -vz which tells grep to find lines that don't match the pattern you give it (-v) and to read the entire input at once (z), so that one match means the whole thing fails:

$ printf 'foo\nbar\n' | grep -zqv foo && echo no || echo yes
yes
$ printf 'foo\nbar\n' | grep -zq foo && echo no || echo yes
no

So, in your case, something like:

run_mongo(){
  mongo foo bar baz 2>&1 | grep -vzq "whatever means failure" 
}

if run_mongo; then
 echo "Worked!"
else
  echo "Failed!"
fi

If you want to avoid reading the whole output, just use another tool. Perl, for example:

mongo foo bar baz 2>&1 | perl -ne 'exit 1 if /whatever means failure/' 
9
  • the only question in my mind - does the pipe clean itself up consistently if the last command dies first? or if any of the non-first commands die first? I would guess no... Commented Mar 26, 2018 at 23:54
  • 1
    @AlexanderMills that's not relevant to the question you're asking, since you said the process fails without actually returning, but no it doesn't. If that's the behavior you want and assuming your default shell is bash, then you need set -o pipefail (see Get exit status of process that's piped to another).
    – terdon
    Commented Mar 27, 2018 at 0:04
  • 1
    @AlexanderMills see help set: "pipefail the return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if no command exited with a non-zero status"
    – muru
    Commented Mar 27, 2018 at 2:51
  • 1
    I don't see why you need the -z option if you want to exit ASAP. Are you just making sure that mongo is allowed to finish?
    – Kusalananda
    Commented Oct 24, 2022 at 15:44
  • 1
    @terdon You probably don't miss anything. It's just me that can't wrap my head around it properly.
    – Kusalananda
    Commented Oct 24, 2022 at 15:53
5

If you need to terminate as quickly as possible:

run_mongo () {
    ! mongo foo bar baz 2>&1 | grep -q 'bad-pattern'
}

This would run mongo and terminate the function as soon as possible if the pattern bad-pattern is found in the output. When the pattern is matched, grep exits immediately, and mongo receives a PIPE signal as soon as it tries to write to the dead pipe.

Due to the !, the exit status of the pipeline will be inverted, i.e. it will be zero if the pattern wasn't found or non-zero if it was found.

If you need to let mongo finish writing its output:

run_mongo () {
    ! mongo foo bar baz 2>&1 | grep 'bad-pattern' > /dev/null
}

This is the same as the first piece of code in this answer, but grep now consumes all output from mongo.

2
  • what is the ! for? Commented Oct 24, 2022 at 16:15
  • 1
    @AlexanderMills It inverts the exit status of the subsequent command or pipeline.
    – Kusalananda
    Commented Oct 24, 2022 at 17:53
0

@terdon's solution looks good, since I am a Node.js fanboy, I just wrote a program like this:

#!/usr/bin/env node

process.stdin.resume().once('data', function () {
   process.exit(1);
});

then in my bash script have:

run_mongo(){
  mongo foo bar baz 2>&1
}

exit_if_match(){
    grep "$1" | "./node_modules/.bin/imminent_suicide"
}

run_mongo "foo" "bar" | exit_if_match "errmsg"
0
echo 'if stdout/stderr matches this' |
    awk '$0 ~ "if stdout/stderr matches this" { exit 1; }' ; echo $?

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .