Prevent grep returning an error when input doesn't match
Solution 1:
Sure:
ps -ef | grep bar | { grep -v grep || true; }
Or even:
ps -ef | grep bar | grep -v grep | cat
Solution 2:
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 selected -
1
means no lines were selected -
2
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
Solution 3:
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'
Solution 4:
Why ask ps
to provide massive amounts of output with -ef
if you only are going to throw away 99% of it? ps
and especially the GNU version is a swiss army knife of handy functionality. Try this:
ps -C bar -o pid= 1>/dev/null
I specify -o pid=
here just because, but in fact it's pointless since we throw away all of stdout anyway. It would be useful if you wanted to know the actual running PID, though.
ps
automatically will return with a non-zero exist status if -C
fails to match anything and with zero if it matches. So you could simply say this
ps -C bar 1>/dev/null && echo bar running || echo bar not running
Or
if ps -C bar 1>/dev/null ; then
echo bar running
else
echo bar not running
fi
Isn't that simpler? No need for grep, not twice or even once.
Solution 5:
foo=`ps -ef | grep bar | grep -v grep` || true