Reputation: 8615
How can I reduce the following bash script?
grep -P "STATUS: (?!Perfect)" recess.txt && exit 1
exit 0
It seems like I should be able to do it with a single command, but I have a total of 3 here.
My program should:
The answer award goes to the tightest script. Thanks!
Program should have exit status 0 for this file:
FILE: styles.css
STATUS: Perfect!
FILE: contour-styles.css
STATUS: Perfect!
Program should have exit status 1 (or non-zero) for this file:
FILE: styles.css
STATUS: Perfect!
FILE: contour-styles.css
STATUS: Busted
FAILURES: 1 failure
Id's should not be styled
1. #asdf
Upvotes: 54
Views: 28466
Reputation: 1502
Node users might want to use a Windows-compatible solution. Here's a NodeJS script which does it:
const { spawnSync } = require('child_process')
const [_node, _thisFile, command, ...args] = process.argv
const subprocess = spawnSync(command, args, { stdio: 'inherit' })
process.exit(subprocess.status === 0 ? 1 : 0)
See the following gist for the full version of the script: https://gist.github.com/mathieucaroff/39a721d6e3959c0a51e035c984c61ac9
Upvotes: 0
Reputation: 15329
Since someone already posted a Puppet solution, I might as well add how to invert a shell command run by Ansible:
- name: Check logs for errors
command: grep ERROR /var/log/cassandra/system.log
register: log_errors
failed_when: "log_errors.rc == 0"
I.e. you just set the failed condition to the return code being 0. So this command fails if we do find the word ERROR in our logs.
I chose this rather than grep -v
as that also inverts grep's output, so we would receive all DEBUG/INFO/WARN lines in log_errors.stdout_lines
which we do not want.
Upvotes: 0
Reputation: 14558
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;
Upvotes: 11
Reputation: 113
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
Upvotes: 1
Reputation: 1631
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
$
Upvotes: 11
Reputation: 6410
Just negating the return value doesn't work in a set -e context. But you can do:
! grep -P "STATUS: (?!Perfect)" recess.txt || false
Upvotes: 7
Reputation: 3097
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:
test 0 -eq 1
will return 1.test 1 -eq 1
will return 0.test 2 -eq 1
will return 1.Which is exactly what I wanted: return 0 if no match is found, and 1 otherwise.
Upvotes: 28
Reputation: 1
I also needed such a solution for writing puppet only if statements and came up with the following command:
/bin/grep --quiet 'root: [email protected]' /etc/aliases; if [ $? -eq 0 ]; then test 1 -eq 2; else test 1 -eq 1; fi;
Upvotes: 0
Reputation: 28200
Just negate the return value.
! grep -P "STATUS: (?!Perfect)" recess.txt
Upvotes: 66
Reputation: 8282
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
Upvotes: 5
Reputation: 272687
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.)
Upvotes: 1