Reputation: 15
Assume a file $tmpdir$results_tmp containing:
==========================
UNIT 1 OK
UNIT 2 OK
UNIT 3 OK
UNIT 4 OK
I want to conditionally grep the lines that begin with a digit, then grep those for "OK". If any of the lines that begin with a digit don't contain "OK", throw an error.
while read -r line
do
if [ grep '^[0-9]' | grep -qv "OK" ] <<< "$line"; then
printf "$line"
results=$results"BAD UNIT DETECTED: $line\n\n"
PASS=false
fi
done < $tmpdir$results_tmp
My assumption is that the first grep pulls out the lines that begin with a digit and the second grep evaluates those to see if they contain "OK", but that doesn't seem to be what's happening.
Can anyone see where I've made my misstep?e
Upvotes: 1
Views: 390
Reputation: 3838
To complete John1024's answer (which is already complete) : the double bracket syntax of Bash provide the operator =~
which allows us to use regular expressions.
So, when you write bash scripts, it's generally not useful to use external commands like grep
or expr
in your blocks (if
, while
, ...).
For example, you could use :
while read -r; do
if [[ $REPLY =~ ^[0-9] && ! $REPLY =~ OK ]]; then
# ...
fi
done < file
Instead of :
while read -r line; do
if grep '^[0-9]' <<<"$line" | grep -qv "OK"; then
# ...
fi
done < file
On the other hand, the ^[0-9]
pattern match only if the line begin whith a numeric character.
This never seems to be the case according to your input sample.
Upvotes: 0
Reputation: 113934
I have four solutions here, two using awk
, one using sed
, and one using a slightly modified version of your bash code.
First, an awk
solution:
awk '!/^[0-9]/ {next} !/OK/ {print "BAD UNIT DETECTED:",$0}' input
I tried this using sample input:
$ cat input
======
1 OK
2 not
text only
3 OK
4 OK
5 no status
And, the result is:
$ awk '!/^[0-9]/ {next} !/OK/ {print "BAD UNIT DETECTED:",$0}' input
BAD UNIT DETECTED: 2 not
BAD UNIT DETECTED: 5 no status
There are two awk
commands. The first is !/^[0-9]/ {next}
. This looks for any line that does not start with a digit (the exclamation point mean "not") and skips it. The second is !/OK/ {print "BAD UNIT DETECTED:",$0}
. This looks any of the remaining lines that do not contain "OK" and prints them with an error message.
awk
In the comments, JS points out that the logic can be reordered:
awk '/^[0-9]/ && !/OK/ {print "BAD UNIT DETECTED:",$0}' input
This looks for all lines which (a) do not begin with a digit, /^[0-9]/
, and also (b) do not contain OK, !/OK/
. The &&
is the awk
notation for logical-and. Any line passing both those tests is printed with the error message.
sed
$ sed '/^[^0-9]/d; /OK/d; s/^/BAD UNIT DETECTED: /' input
BAD UNIT DETECTED: 2 not
BAD UNIT DETECTED: 5 no status
There are three sed
commands here. The first /^[^0-9]/d
skips over any lines not beginning with a digit. The next /OK/d
slips over any of the remaining lines that have "OK" in them. For any line that is left, an error message is prepended and it is printed.
A problem with your code is here:
[ grep '^[0-9]' | grep -qv "OK" ] <<< "$line"
The leading [
signals the start of a test
command but what follows is not a valid test. Since grep
sets proper exit codes, a test
command is not needed anyway. The above probably should be replaced with:
grep '^[0-9]' <<<"$line" | grep -qv "OK"
or,
echo "$line | grep '^[0-9]' | grep -qv "OK"
A minimally-modified working version of your bash code is:
while read -r line
do
if grep '^[0-9]' <<<"$line" | grep -qv "OK"
then
printf "$line\n"
results="${results}BAD UNIT DETECTED: $line\n\n"
PASS=false
fi
done <input
Note that the results
variable contains literal characters backslash and n. This may or may not be what you want.
Upvotes: 3