Bash: One-liner to exit with the opposite status of a grep command?

做~自己de王妃 提交于 2019-12-04 22:18:16

Just negate the return value.

! grep -P "STATUS: (?!Perfect)" recess.txt
cincodenada

I came across this, needing an onlyif statement for Puppet. As such, Tgr's bash solution wouldn't work, and I didn't want to expand the complexity as in Christopher Neylan's answer.

I ended up using a version inspired by Henri Schomäcker's answer, but notably simplified:

grep -P "STATUS: (?!Perfect)" recess.txt; test $? -eq 1

Which very simply inverts the exit code, returning success only if the text is not found:

  • If grep returns 0 (match found), test 0 -eq 1 will return 1.
  • If grep returns 1 (no match found), test 1 -eq 1 will return 0.
  • If grep returns 2 (error), test 2 -eq 1 will return 1.

Which is exactly what I wanted: return 0 if no match is found, and 1 otherwise.

To make it work with set -e surround it in a sub-shell with ( and ):

$ cat test.sh 
#!/bin/bash

set -ex
(! ls /tmp/dne)
echo Success
$ ./test.sh 
+ ls /tmp/dne
ls: cannot access /tmp/dne: No such file or directory
+ echo Success
Success
$ mkdir /tmp/dne
$ ./test.sh 
+ ls /tmp/dne
$ 

You actually don't need to use exit at all. Logically, no matter what the result of grep, your script is going to exit anyway. Since the exit value of a shell script is the exit code of the last command that was run, just have grep run as the last command, using the -v option to invert the match to correct the exit value. Thus, your script can reduce to just:

grep -vqP "STATUS: (?!Perfect)" recess.txt

EDIT:

Sorry, the above does not work when there are other types of lines in the file. In the interest of avoiding running multiple commands though, awk can accomplish the entire shebang with something like:

awk '/STATUS: / && ! /Perfect/{exit 1}' recess.txt

If you decide you want the output that grep would have provided, you can do:

awk '/^STATUS: / && ! /Perfect/{print;ec=1} END{exit ec}' recess.txt

if anyone gets here looking for a bash return code manipulation:

(grep <search> <files> || exit 0 && exit 123;)

this will return 0 (success) when grep finds nothing, and return 123 (failure) when it does. The parenthesis are in case anyone test it as is on the shell prompt. with parenthesis it will not logout on the exit, but just exit the subshell with the same error code.

i use it for a quick syntax check on js files:

find src/js/ -name \*js -exec node \{\} \; 2>&1 | grep -B 5 SyntaxError || exit 0 && exit 1;

Just negating the return value doesn't work in a set -e context. But you can do:

! grep -P "STATUS: (?!Perfect)" recess.txt || false

Use the special ? variable:

grep -P "STATUS: (?!Perfect)" recess.txt
exit $((1-$?))

(But note that grep may also return 2, so it's not clear what you'd want to occur in such cases.)

The problem with the grep answers is that if the file is empty you also get a clean response, as if the file had a perfect. So personally I gave up on grep for this and used awk.

awk 'BEGIN{ef=2}; /STATUS: Perfect/{ ef=0;}; /STATUS: Busted/{ print;eff=3;}; END{exit (ef+eff)}' a.txt ; echo $?

This has exit status:
 0 :  Perfect and !Busted
 2 : !Perfect and  Busted
 3 :  Perfect and  Busted
 5 : !Perfect and !Busted
[ $(grep -c -P "STATUS: (?!Perfect)" recess.txt) -eq 0 ]
Henri Schomäcker

I also needed such a solution for writing puppet only if statements and came up with the following command:

/bin/grep --quiet 'root: root@ourmasterdomain.de' /etc/aliases; if [ $? -eq 0 ]; then test 1 -eq 2; else test 1 -eq 1; fi;

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!