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
Bash Script Read All the Files in Directory
How to Convert String to Number in Gnuplot
Replacing Environment Variables in a Properties File
How to Convert Spaces to Tabs in Vim or Linux
Tcp_Tw_Reuse VS Tcp_Tw_Recycle:Which to Use (Or Both)
Installing R from Cran Ubuntu Repository: No Public Key Error
Differencebetween Clock_Monotonic & Clock_Monotonic_Raw
Sort a Tab Delimited File Based on Column Sort Command Bash
Munmap() Failure with Enomem with Private Anonymous Mapping
Looping a Video with Gstreamer and Gst-Launch
Using Linux How to Pass the Contents of a File as a Parameter to an Executable
How to Emulate the Raspberry Pi 2 on Qemu
Change Directory and Execute File in One Command
How to See Top Processes Sorted by Actual Memory Usage
Run Bash Commands from Txt File