Prevent grep returning an error when input doesn't match
Sure:
ps -ef | grep bar | { grep -v grep || true; }
Or even:
ps -ef | grep bar | grep -v grep | cat
Short answer
Write
ps -ef | grep bar | { grep -v grep || test $? = 1; }
if you are using set -e
.
If you use bash's pipefail
option (set -o pipefail
), remember to apply the exception handling (||test
) to every grep
in the pipeline:
ps -ef | { grep bar || test $? = 1; } | { grep -v grep || test $? = 1; }
In shell scripts I suggest you to use the ”catch-1-grep“ (c1grep) utility function:
c1grep() { grep "$@" || test $? = 1; }
Explained
grep
's exit status is either 0, 1 or 2: [1]
0
means a line is selected1
means no lines were selected2
means an error occurred
grep
can also return other codes if it's interrupted by a signal (e.g. 130
for SIGINT).
Since we only want to ignore exit status 1
, we use test
to suppress that specific exit status.
- If
grep
returns0
,test
is not run. - If
grep
returns1
,test
is run and returns0
. - If
grep
returns any other value,test
is run and returns1
.
In the last case, the script will exit immediately due to set -e
or set -o pipefail
. However, if you don't care about grep
errors at all, you can of course write
ps -ef | grep bar | { grep -v grep || true; }
as suggested by Sean.
[additional] usage in shell scripts
In shell scripts, if you are using grep
a lot, I suggest you to define an utility function:
# "catch exit status 1" grep wrapper
c1grep() { grep "$@" || test $? = 1; }
This way your pipe will get short & simple again, without losing the features of set -e
and set -o pipefail
:
ps -ef | c1grep bar | c1grep -v grep
FYI:
- I called it
c1grep
to emphasize it's simply catching exit status1
, nothing else. - I could have called the function
grep
instead (grep() { env grep "$@" ...; }
), but I prefer a less confusing and more explicit name,c1grep
.
[additional] ps
+ grep
So if you want to know how to avoid grep -v grep
or even the | grep
part of ps|grep
at all, take a look at some of the other answers; but this is somewhat off-topic imho.
[1] grep
manpage
A good trick to avoid grep -v grep
is this:
ps -ef | grep '[b]ar'
That regular expression only matches the string "bar". However in the ps
output, the string "bar" does not appear with the grep process.
In the days before I learned about pgrep
, I wrote this function to automate the above command:
psg () {
local -a patterns=()
(( $# == 0 )) && set -- $USER
for arg do
patterns+=("-e" "[${arg:0:1}]${arg:1}")
done
ps -ef | grep "${patterns[@]}"
}
Then,
psg foo bar
turns into
ps -ef | grep -e '[f]oo' -e '[b]ar'