Bash string (command output) equality test - bash

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.

Related

Bash if doesn't match the content

My code:
#!/bin/bash
content="My sms content"
nomer="My Phone number"
if [[ -n "$content" && -n "$nomer" ]]; then
echo "it passes the first filter..."
if [ "$(echo $content| awk '{print tolower($0)}')" = "My sms content" ]; then
echo "Yes it matches the content"
else
echo "the variable doesn't match the content"
fi
fi
It displays nothing when I run the code. What I want is that it displays Yes it matches the content How do I do that?
UPDATE as #BlueMoon answer I updated my code but it still says the variable doesn't match the content
I want it to match the content
As well as the problem of this passing only if both strings are empty:
if [[ -z "$content" && -z "$nomer" ]]
(you probably meant -n which is the non-empty variant), you also have the problem that a string converted to lower-case will never contain a capital M:
if [ "$(echo $content| awk '{print tolower($0)}')" = "My sms content" ]
(you probably meant "my sms content").
In any case, if you're using a relatively modern version of bash, it has case conversion built in so you don't have to use external processes. The following transcript shows the four possibilities, upper-case first, upper-case all, lower-case first and lower-case all:
pax> word='abcde' ; echo ${word^}
Abcde
pax> word='abcde' ; echo ${word^^}
ABCDE
pax> word='ABCDE' ; echo ${word,}
aBCDE
pax> word='ABCDE' ; echo ${word,,}
abcde
I gather the rationale here was that the caret ^ points up while the comma , points down. So you can simply use:
if [[ "${content,,}" == "my sms content" ]]
-z tests for being zero length. So what you're comparing is to check that both strings are empty.
This line
if [[ -z "$content" && -z "$nomer" ]]; then
says if both strings are of non-zero then do it.
What you probably meant is if both are non-empty:
if [[ -n "$content" && -n "$nomer" ]]; then
From manual:
-z string
True if the length of string is zero.
string
-n string
True if the length of string is non-zero.

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).

Improve script output

I have written a function which checks whether one or more variables passed as arguments are empty (got the idea from here)
Here is what the script looks like:
#!/bin/bash
is_variable_defined?() {
for VAR in "$#"; do
: "${!VAR? "ERROR: $VAR is undefined."}"
done
}
TEST='a'
is_variable_defined? TEST TEST2
And here is the output:
/path/to/script.sh: line 4: !VAR: ERROR: TEST2 is undefined.
However what I would like to output is:
TEST2 is undefined
I have tried tweaking the : "${!VAR? "ERROR: $VAR is undefined."}" line but whatever I do it breaks.
Does anybody know what to modify in the script to get the output I want?
Are you seeing if the variable is undefined or simply a null string?
Null Test
is_variable_defined() {
for var in $#
do
if [ -z ${!var} ] # Or if [[ ${!var} ]]
then
echo "Var $var has no value"
fi
done
}
Undefined Test
is_variable_defined() {
for var in $#
do
if ! set | grep -q "^$var="
then
echo "Var $var is not set"
fi
done
The last is looking at the output of the set command to see if the variable is listed there. The -q will make grep quiet. And you look at the exit status to see if it found the string. If your grep doesn't support the -q parameter, you can try grep "^$var=" 2> /dev/null.
Using the [[ $foo ]] syntax for testing won't necessarily work. The below will print $foo is not set even though it was set to an empty string:
foo=""
if [[ $foo ]]
then
echo "\$foo = '$foo'"
else
echo "\$foo is not set"
fi

I want a to compare a variable with files in a directory and output the equals

I am making a bash script where I want to find files that are equal to a variable. The equals will then be used.
I want to use "mogrify" to shrink a couple of image files that have the same name as the ones i gather from a list (similar to "dpkg -l"). It is not "dpkg -l" I am using but it is similar. My problem is that it prints all the files not just the equals. I am pretty sure this could be done with awk instead of a for-loop but I do not know how.
prog="`dpkg -l | awk '{print $1}'`"
for file in $dirone* $dirtwo*
do
if [ "basename ${file}" = "${prog}" ]; then
echo ${file} are equal
else
echo ${file} are not equal
fi
done
Could you please help me get this working?
First, I think there's a small typo. if [ "basename ${file}" =... should have backticks inside the double quotes, just like the prog=... line at the top does.
Second, if $prog is a multi-line string (like dpkg -l) you can't really compare a filename to the entire list. Instead you have to compare one item at a time to the filename.
Here's an example using dpkg and /usr/bin
#!/bin/bash
progs="`dpkg -l | awk '{print $2}'`"
for file in /usr/bin/*
do
base=`basename ${file}`
for prog in ${progs}
do
if [ "${base}" = "${prog}" ]; then
echo "${file}" matches "${prog}"
fi
done
done
The condition "$file = $prog" is a single string. You should try "$file" = "$prog" instead.
The following transcript shows the fix:
pax> ls -1 qq*
qq
qq.c
qq.cpp
pax> export xx=qq.cpp
pax> for file in qq* ; do
if [[ "${file} = ${xx}" ]] ; then
echo .....${file} equal
else
echo .....${file} not equal
fi
done
.....qq equal
.....qq.c equal
.....qq.cpp equal
pax> for file in qq* ; do
if [[ "${file}" = "${xx}" ]] ; then
echo .....${file} equal
else
echo .....${file} not equal
fi
done
.....qq not equal
.....qq.c not equal
.....qq.cpp equal
You can see in the last bit of output that only qq.cpp is shown as equal since it's the only one that matches ${xx}.
The reason you're getting true is because that's what non-empty strings will give you:
pax> if [[ "" ]] ; then
echo .....equal
fi
pax> if [[ "x" ]] ; then
echo .....equal
fi
.....equal
That's because that form is the string length checking variation. From the bash manpage under CONDITIONAL EXPRESSIONS:
string
-n string
True if the length of string is non-zero.
Update:
The new code in your question won't quite work as expected. You need:
if [[ "$(basename ${file})" = "${prog}" ]]; then
to actually execute basename and use its output as the first part of the equality check.
you can use case/esac
case "$file" in
"$prog" ) echo "same";;
esac

Shell command - condition based on output of command?

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

Resources