The Return Code from 'Grep' Is Not as Expected on Linux

The return code from 'grep' is not as expected on Linux

According to man grep page, -c flag is for

-c, --count
Suppress normal output; instead print a count of matching lines for each input file.

So what you are seeing is the count of the match and not to be confused with the exit code of the grep match. The code 1 is because of no lines matching from the input.

Have a look at the other case,

echo 'No' | grep -c No
1

echo $?
0

Also to read on EXIT CODES on man grep page,

EXIT STATUS
Normally the exit status is 0 if a line is selected, 1 if no lines were selected, and 2 if an error occurred.

The exit status code for the 'grep' command

Remember that grep is line based. If any line matches, you got a match. (In your first case test.zip matches (more precisely: you used with -v therefore you have asked for lines that do not match your pattern, and test.zip does exactly that, i.e. does not match your pattern. As a result your grep call was successful). Compare

$ grep -vE '^[.]' <<<$'.\na'; echo $?
a
0

with

$ grep -vE '^[.]' <<<$'.\n.'; echo $?
1

Note how the first command outputs the line a, that is it has found a match, which is why the exit status is 0. Compare that with the second example, where no line was matched.

References

<<< is a here string:

Here Strings
A variant of here documents, the format is:

[n]<<<word

The word undergoes brace expansion, tilde expansion, parameter and
variable expansion, command substitution, arithmetic expansion, and
quote removal. Pathname expansion and word splitting are not per-
formed. The result is supplied as a single string, with a newline
appended, to the command on its standard input (or file descriptor n if
n is specified).
   $ cat <<<'hello world'
hello world

$'1\na' is used to get a multi line input (\n is replaced by newline within $'string', for more see man bash).

$ echo $'1\na'
1
a

How to use return status value for grep?

I resolved this by adding brackets around the grep and $?

(grep 'Unable' check_error_output.txt && echo $?) | tail -1

Terminal - Why the exit command of grep is 0 even if a match is not found?

This is the problem:

grep -E '^nothing' List.txt | echo $?

By using single | you are sending output of grep to echo which will always print exit status of previous command and that will always be 0 whether pattern is found or not.

You can use grep -q:

grep -qE '^nothing' List.txt

As per man grep:

 -q, --quiet, --silent
Quiet mode: suppress normal output. grep will only search a file until a match
has been found, making searches potentially less expensive.

How can I get the return value and matched line by grep in bash at once?

You can avoid the double use of grep by storing the search output in a variable and seeing if it is not empty.

Your version of the script without double grep.

#!/bin/bash

grepOutput="$(grep 'match_word' file)"

if [ ! -z "$grepOutput" ]; then
read a b <<< "${grepOutput}"
fi

An optimization over the above script ( you can remove the temporary variable too)

#!/bin/bash

grepOutput="$(grep 'match_word' file)"

[[ ! -z "$grepOutput" ]] && (read a b <<< "${grepOutput}")

Using double-grep once for checking if-condition and once to parse the search result would be something like:-

#!/bin/bash

if grep -q 'match_word' file; then
grepOutput="$(grep 'match_word' file)"
read a b <<< "${grepOutput}"
fi

Get exit code when piping into grep -v

Leveraging set -o pipefail, the following should work:

( set -o pipefail; ./fail.sh | grep -v hello )

You can then test the value in $?:

( set -o pipefail; ./fail.sh | grep -v hello ); if [[ "$?" -eq "1" ]]; then echo success; else echo bummer; fi 

It should output:

goodbye
success

What is happening and why does this work?

As noted in the OP, pipelines normally only return a failure (non-zero return code) if the last command errors. Using set -o pipefail causes a pipeline of commands to produce a failure return code if any command in the pipeline errors. The failure return code that the pipeline passes is the return code of the last failed command.

You can test this by updating your script to:

#!/bin/sh

echo "hello"
echo "goodbye"
exit 5

then run the following:

( set -o pipefail; ./fail.sh | grep -v hello ); echo $?

It should output:

goodbye
5

The above illustrates that set -o pipefail is not just exiting with a non-zero return code but that it is relaying the last non-zero return code verbatim.



Related Topics



Leave a reply



Submit