Grep match exact string - regex

file.txt
example=0x12; non-match
grep -w "example=0x0" file.txt
I am trying to match exact string using grep and it looks like
echo $? always returns 1.
In this case, the output should have been 0. I have tried -F too.

You need the -q option if you're just checking the exit status. See below for examples
$ grep -qw "A" <<< "AAA"; echo $?
1
$ grep -qw "A" <<< "A"; echo $?
0
first word match fails, exit 1. Second one succeeds, exit 0.

Related

Check if any replacement done by `perl -i -pe`

In GNU sed, I can display the result of successful substitution of the search pattern. Simple example as the following:
echo -e "nginx.service\nmariadb.service\nphp-fpm.service" > something.conf;
sed -ri 's|(mariadb)(\.service)|postgresql-9.4\2|w sed-output.log' something.conf;
[[ -s sed-output.log ]] && echo "Pattern found and modified. $(cat sed-output.log)" || echo "Pattern not found.";
Because sed has limitation while dealing with multilines, I switched to perl.
echo -e "nginx.service\nmariadb.service\nphp-fpm.service" > something.conf;
perl -i -pe 's|(mariadb)(\.service)|postgresql-9.4\2|' something.conf;
The code above did the same like sed, but how can I get the modified content ("postgresql-9.4.service") into a file, or printed out?
Basically what I would like to achieve is, after the script has been executed, it tells me if it's successful (and what actually substituted) and if not, I'll display a message of what couldn't be found and replaced.
Edit:
Highlighted that I want to get (only-the-) modified content, which indicates that my script is successful. Because with perl -i -pe 's/pattern/replace/' file, I couldn't know if it return true or false. Of course I can simple do grep -E "/pettern/" to find out, but that's not the question.
This code will throw an exit code equal to 0 when replacement is done:
$ perl -i -pe '$M += s|(mariadb)(\.service)|postgresql-9.4\2|;END{exit 1 unless $M>0}' something.conf
$ echo $?
0
When NO substitution is done, return code will be 1:
$ perl -i -pe '$M += s|(maria)(\.service)|postgresql-9.4\2|;END{exit 1 unless $M>0}' something.conf
$ echo $?
1
From Perl documentation
An END code block is executed as late as possible, that is, after perl
has finished running the program and just before the interpreter is
being exited, even if it is exiting as a result of a die() function.
(But not if it's morphing into another program via exec, or being
blown out of the water by a signal--you have to trap that yourself (if
you can).) You may have multiple END blocks within a file--they will
execute in reverse order of definition; that is: last in, first out
(LIFO). END blocks are not executed when you run perl with the -c
switch, or if compilation fails.
Number of replacements returned from s operator
s/PATTERN/REPLACEMENT/msixpodualngcer
Searches a string for a pattern, and if found, replaces that pattern
with the replacement text and returns the number of substitutions
made.
It isn't as tidy in Perl because you have to open your log file explicitly, and for a one-liner that has to be in a BEGIN block. But Perl's s/// returns the number of changes made, so you can test it for truth
Note also that $2 is better than \2 in Perl, as the latter represents a character with code point 2, or Unicode U+0002 START OF TEXT
perl -i -pe ' BEGIN { open F, ">perl-output.log" } print F $_ if s|(mariadb)(\.service)|postgresql-9.4$2| ' something.conf
You can check the output directly if you only print the substituted lines:
if [[ -z $(sed -n 's/mariadb\(\.service\)/postgresql-9.4\1/p' something.conf) ]]; then
echo nope
fi

grep with extended regex over multiple lines

I'm trying to get a pattern over multiple lines. I would like to ensure the line I'm looking for ends in \r\n and that there is specific text that comes after it at some point. The two problems I've had are I often get unmatched parenthesis in groupings or I get a positive match when there is none. Here are two simple examples.
echo -e -n "ab\r\ncd" | grep -U -c -z -E $'(\r\n)+.*TEST'
grep: Unmatched ( or \(
What exactly is unmatched there? I don't get it.
echo -e -n "ab\r\ncd" | grep -U -c -z -E $'\r\n.*TEST'
1
There is no TEST in the string, so why does this return a count of 1 for matches?
I'm using grep (GNU grep) 2.16 on Ubuntu 14. Thanks
Instead of -E you can use -P for PCRE support in gnu grep to use advanced regex like this:
echo -ne "ab\r\ncd" | ggrep -UczP '\r\n.*TEST'
0
echo -ne "ab\r\ncd" | ggrep -UczP '\r\n.*cd'
1
grep -E matches only in single line input.

Print a part of string regex bash

From this content (in a file):
myspecificBhost.fqdn.com myspecificaBhost.fqdn.com myspecificzBhost.fqdn.com
I need to print the next 4 characters from the "B":
Bhost
I tried:
echo ${var:position1:lenght}
but position 1 is never equal
Using BASH regex:
s='myspecificBhost.fqdn.com myspecificaBhost.fqdn.com myspecificzBhost.fqdn.com'
[[ "$s" =~ (B[a-z][a-z][a-z][a-z]) ]] && echo "${BASH_REMATCH[1]}"
Bhost
try sed command:
sed -nr '/.*c(.{4,6}).*/s//\1/p' input.txt | cut -c2-6
RESULT:
Bhost
With grep command:
cat input.txt | grep -o B.... | head -1
RESULT:
Bhost
Try with:
cat file | grep -o B....
Bash using parameter substitution. Outputs the 4 characters
after the first 'B':
text='myspecificBhost.fqdn.com myspecificaBhost.fqdn.com myspecificzBhost.fqdn.com'
text=${text#*B}
text=${text:0:4}
echo "${text}"
Output:
host
To get the leading 'B' use
echo "B${text}"

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

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:
Read recess.txt
Exit 1 (or non-zero) if it contains a line with "STATUS: " of NOT "Perfect"
Exit 0 if no such line exists (i.e. all "STATUS: " lines are "Perfect")
The answer award goes to the tightest script. Thanks!
Example files
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
Just negate the return value.
! grep -P "STATUS: (?!Perfect)" recess.txt
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.
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;
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
$
Just negating the return value doesn't work in a set -e context. But you can do:
! grep -P "STATUS: (?!Perfect)" recess.txt || false
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
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 ]
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;
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.

Get total number of matches for a regex via standard unix command

Let's say that I want to count the number of "o" characters in the text
oooasdfa
oasoasgo
My first thought was to do grep -c o, but this returns 2, because grep returns the number of matching lines, not the total number of matches. Is there a flag I can use with grep to change this? Or perhaps I should be using awk, or some other command?
This will print the number of matches:
echo "oooasdfa
oasoasgo" | grep -o o | wc -l
you can use the shell (bash)
$ var=$(<file)
$ echo $var
oooasdfa oasoasgo
$ o="${var//[^o]/}"
$ echo ${#o}
6
awk
$ awk '{m=gsub("o","");sum+=m}END{print sum}' file
6