Shell command - condition based on output of command? - shell

I'm trying to run some shell command if a string is not present in a text file. If I paste this line into the command line if gives me an error.
if [ $(cat textfile.txt | grep "search string") -eq "" ]; then; echo "some string"; fi;
Error:
-bash: [: -eq: unary operator expected

If you use [] for comparison you need to use = instead of -eq. You also need some quotes.
if [ "$(cat textfile.txt | grep 'search string')" = "" ]; then; echo "some string"; fi;
Note that grep can take a filename as argument so the cat is unnecessary. You can also directly use the return value of grep: grep returns 1 if the search string is not found.
if [ "$(grep 'search string' textfile.txt)" ]; then
echo "some string";
fi
An even more compact way would be to use logical and &&.
grep "search string" textfile.txt && echo "some string"

The grep command will return 0 if the requested lines are found (1 if not, 2 if an error), so you can just use:
grep "search string" textfile.txt >/dev/null 2>&1
if [[ $? -ne 0 ]] ; then
echo 'Not found'
fi
If you really wanted to use strings (and you probably shouldn't), you should quote them so that you don't get too many arguments for the [ command:
if [ "$(cat textfile.txt | grep 'search string')" == "" ] ; then
echo "It's not there!"
fi

grep -F -q -e 'search string' textfile.txt || echo 'Not found'
Note:
-F prevents the interpretation of the search string as a regular expression.
-q suppresses all output and returns immediately after the first instance was found, making the search much faster if the string occurs at the beginning of a large file.
-e specifies the pattern explicitly, allowing patterns that start with a dash.
Use single quotes unless you want variable substitutions.

No need for the square brackets in this case. Since [ is actually a command, any command can be used where you would use it. So here, we can use grep. There's no need to use cat since grep will accept filenames as arguments. Also, you have two too many semicolons.
if grep -q "search string" textfile.txt; then echo "some string"; fi
or
if grep "search string" textfile.txt > /dev/null 2>&1; then echo "some string"; fi

Related

Finding presence of substring within a string in BASH

I have a script that is trying to find the presence of a given string inside a file of arbitrary text.
I've settled on something like:
#!/bin/bash
file="myfile.txt"
for j in `cat blacklist.txt`; do
echo Searching for $j...
unset match
match=`grep -i -m1 -o "$j" $file`
if [ $match ]; then
echo "Match: $match"
fi
done
Blacklist.txt contains lines of potential matches, like so:
matchthis
"match this too"
thisisasingleword
"This is multiple words"
myfile.txt could be something like:
I would matchthis if I could match things with grep. I really wish I could.
When I ask it to match this too, it fails to matchthis. It should match this too - right?
If I run this at a bash prompt, like so:
j="match this too"
grep -i -m1 -o "$j" myfile.txt
...I get "match this too".
However, when the batch file runs, despite the variables being set correctly (verified via echo lines), it never greps properly and returns nothing.
Where am I going wrong?
Wouldn't
grep -owF -f blacklist.txt myfile.txt
instead of writing an inefficient loop, do what you want?
Would you please try:
#!/bin/bash
file="myfile.txt"
while IFS= read -r j; do
j=${j#\"}; j=${j%\"} # remove surrounding double quotes
echo "Searching for $j..."
match=$(grep -i -m1 -o "$j" "$file")
if (( $? == 0 )); then # if match
echo "Match: $match" # then print it
fi
done < blacklist.txt
Output:
Searching for matchthis...
Match: matchthis
Searching for match this too...
Match: match this too
match this too
Searching for thisisasingleword...
Searching for This is multiple words...
I wound up abandoning grep entirely and using sed instead.
match=`sed -n "s/.*\($j\).*/\1/p" $file
Works well, and I was able to use unquoted multiple word phrases in the blacklist file.
With this:
if [ $match ]; then
you are passing random arguments to test. This is not how you properly check for variable net being empty. Use test -n:
if [ -n "$match" ]; then
You might also use grep's exit code instead:
if [ "$?" -eq 0 ]; then
for ... in X splits X at spaces by default, and you are expecting the script to match whole lines.
Define IFS properly:
IFS='
'
for j in `cat blacklist.txt`; do
blacklist.txt contains "match this too" with quotes, and it is read like this by for loop and matched literally.
j="match this too" does not cause j variable to contain quotes.
j='"match this too"' does, and then it will not match.
Since whole lines are read properly from the blacklist.txt file now, you can probably remove quotes from that file.
Script:
#!/bin/bash
file="myfile.txt"
IFS='
'
for j in `cat blacklist.txt`; do
echo Searching for $j...
unset match
match=`grep -i -m1 -o "$j" "$file"`
if [ -n "$match" ]; then
echo "Match: $match"
fi
done
Alternative to the for ... in ... loop (no IFS= needed):
while read; do
j="$REPLY"
...
done < 'blacklist.txt'

How to make a regex match with grep in shell script?

I am trying to create a shell script that matches a string with a predefined regex. I have the following code:
#!/bin/sh
name="Name of string to match. #123"
(echo "$name" | grep -Eq ^[#]\d+$) && echo "matched" || echo "did not
match"
I am always getting a "did not match" message, even though I think the string in the example should match the regex. Can anyone realize what I am doing wrong? Is the regex wrong or is it the call to grep?
Wrap the regex in quotes:
(echo "$name" | grep -Eq '^[#]\d+$') && echo "matched" || echo "did not match"
If you expect the sample input to match, remove the start-of-input anchor ^, ie use '[#]\d+$' as your regex (which would match anything ending in # then digits).

How can I get the return value and matched line by grep in bash at once?

I am learning bash. I would like to get the return value and matched line by grep at once.
if cat 'file' | grep 'match_word'; then
match_by_grep="$(cat 'file' | grep 'match_word')"
read a b <<< "${match_by_grep}"
fi
In the code above, I used grep twice. I cannot think of how to do it by grep once. I am not sure match_by_grep is always empty even when there is no matched words because cat may output error message.
match_by_grep="$(cat 'file' | grep 'match_word')"
if [[ -n ${match_by_grep} ]]; then
# match_by_grep may be an error message by cat.
# So following a and b may have wrong value.
read a b <<< "${match_by_grep}"
fi
Please tell me how to do it. Thank you very much.
You can avoid the double use of grep by storing the search output in a variable and seeing if it is not empty.
Your version of the script without double grep.
#!/bin/bash
grepOutput="$(grep 'match_word' file)"
if [ ! -z "$grepOutput" ]; then
read a b <<< "${grepOutput}"
fi
An optimization over the above script ( you can remove the temporary variable too)
#!/bin/bash
grepOutput="$(grep 'match_word' file)"
[[ ! -z "$grepOutput" ]] && (read a b <<< "${grepOutput}")
Using double-grep once for checking if-condition and once to parse the search result would be something like:-
#!/bin/bash
if grep -q 'match_word' file; then
grepOutput="$(grep 'match_word' file)"
read a b <<< "${grepOutput}"
fi
When assigning a variable with a string containing a command expansion, the return code is that of the (rightmost) command being expanded.
In other words, you can just use the assignment as the condition:
if grepOutput="$(cat 'file' | grep 'match_word')"
then
echo "There was a match"
read -r a b <<< "${grepOutput}"
(etc)
else
echo "No match"
fi
Is this what you want to achieve?
grep 'match_word' file ; echo $?
$? has a return value of the command run immediately before.
If you would like to keep track of the return value, it will be also useful to have PS1 set up with $?.
Ref: Bash Prompt with Last Exit Code

Finding a part of a string in another string variable in bash

I have an issue in finding a part of string variable in another string variable, I tried many methods but none worked out..
for example:
echo -e " > Required_keyword: $required_keyword"
send_func GUI WhereAmI
echo -e " > FUNCVALUE: $FUNCVALUE"
flag=`echo $FUNCVALUE|awk '{print match($0,"$required_keyword")}'`;
if [ $flag -gt 0 ];then
echo "Success";
else
echo "fail";
fi
But it always gives fail though there are certain words in variable which matches like
0_Menu/BAA_Record ($required_keyword output string)
Trying to connect to 169.254.98.226 ... OK! Executing sendFunc GUI
WhereAmI Sent Function WhereAmI [OK PageName:
"_0_Menu__47__BAA_Record" ($FUNCVALUE output string)
As we can see here the BAA_Record is common in both of the output still, it always give FAIL
The output echo is
> Required_keyword: 0_Menu/BAA_Record
> FUNCVALUE:
Trying to connect to 169.254.98.226 ... OK!
Executing sendFunc GUI WhereAmI
Sent Function WhereAmI [OK]
PageName: "_0_Menu__47__BAA_Record"
Bash can do wildcard and regex matches inside double square brackets.
if [[ foobar == *oba* ]] # wildcard
if [[ foobar =~ fo*b.r ]] # regex
In your example:
if [[ $FUNCVALUE = *$required_keyword* ]]
if [[ $FUNCVALUE =~ .*$required_keyword.* ]]
Not sure if I understand what you want, but if you need to find out if there's part of string "a" present in variable "b" you can use simply just grep.
grep -q "a" <<< "$b"
[[ "$?" -eq 0 ]] && echo "Found" || echo "Not found"
EDIT: To clarify, grep searches for string a in variable b and returns exit status (see man grep, hence the -q switch). After that you can check for exit status and do whatever you want (either with my example or with regular if statement).

Bash string (command output) equality test

I have a simple script to check whether webpage contains a specified string. It looks like:
#!/bin/bash
res=`curl -s "http://www.google.com" | grep "foo bar foo bar" | wc -l`
if [[ $res == "0" ]]; then
echo "OK"
else
echo "Wrong"
fi
As you can see, I am looking to get "OK", but got a "Wrong".
What's wrong with it?
If I use if [ $res == "0" ], it works. If I just use res="0" instead of res=curl..., it also can obtain the desired results.
Why are there these differences?
You could see what res contains: echo "Wrong: res=>$res<"
If you want to see if some text contains some other text, you don't have to look at the length of grep output: you should look at grep's return code:
string="foo bar foo bar"
if curl -s "http://www.google.com" | grep -q "$string"; then
echo "'$string' found"
else
echo "'$string' not found"
fi
Or even without grep:
text=$(curl -s "$url")
string="foo bar foo bar"
if [[ $text == *"$string"* ]]; then
echo "'$string' found"
else
echo "'$string' not found in text:"
echo "$text"
fi
I found the answer in glenn jackman's help.
I get the following points in this question:
wc -l 's output contains whitespaces.
Debugging with echo "$var" instead of echo $var
[[ preserves the literal value of all characters within the var.
[ expands var to their values before perform, it's because [ is actually the test cmd, so it follows Shell Expansions rules.

Resources