Bash comparison operator always true - bash

I'm trying to write a small script to compare my external IP (first three bytes) with the one below:
#!/bin/bash
MYFILE=/home/me/.config/i3/pia
while true
do
IP_EX=$(wget http://ipinfo.io/ip -qO - | cut -d"." -f1,2,3)
if [[ "$IP_EX"=="173.199.65" ]]
then
echo file created
touch $MYFILE
else
echo file deleted
rm -f $MYFILE
fi
echo sleeping
sleep 4
done
This always returns file created, and the else statement is never executed. This is the case even if I replace the $IP_EX with whatever. Why is that?

Bash commands are sensitive to spaces. You need to add spaces around ==.
Observe that this gives the wrong answer:
$ IP_EX=abc; [[ "$IP_EX"=="173.199.65" ]] && echo True
True
By contrast, this version, with spaces, works correctly:
$ IP_EX=abc; [[ "$IP_EX" == "173.199.65" ]] && echo True
$
The problem is that bash sees "$IP_EX"=="173.199.65" as a single string. When given such a single argument, [[ returns true if the string is not empty and false if it is empty:
$ [[ "" ]] && echo True
$ [[ "1" ]] && echo True
True
With the spaces added in, bash sees "$IP_EX" == "173.199.65" as three arguments with the middle argument being ==. It therefore tests for equality. This is what you want.

Related

Why is empty string changed into -n expression in bash

Taken this snippet:
$ [[ ""=="foo" ]] && echo yes || echo no
+ [[ -n ==foo ]]
+ echo yes
yes
How does [[ ""=="foo" ]] turn into [[ -n ==foo ]] ?
The RC was of course missing spaces around == - after adding them, it works as expected:
$ [[ "" == "foo" ]] && echo yes || echo no
+ [[ '' == \f\o\o ]]
+ echo no
no
But still i cannot understand why it behaved like this?
It's not changing the empty string into -n.
The string ""=="foo" is equivalent to the string ==foo. The trace output always shows strings in their simplest format, without unnecessary quotes.
A conditional expression that just contains a single string with no operators is true if the string is not empty. That's what the -n operator tests, so the -x expansion shows it that way.
Any operand that isn't preceded or followed by an operator is treated to have an equal operation as -n <operand>. Operators also need to be isolated with spaces to be distinguished. For a list of operators run help test. Also run help [[ to see how the keyword is different from the [ and test builtins.

Why is [[ "$input" == name1* || name2* ]] never false?

I'm a little befuddled with a script I've been writing - and would appreciate some help!
This is one of those cases where each command seems to work fine on their own, but not so when put together into a script.
Here's a gist of what I'm trying to do:
input=$1
single_func () {
command "$input"
}
multi_func () {
xargs < $input -n 1 single_func
}
if [[ "$input" == name1* || name2* ]];
then
single_func
elif [[ -f "$input" ]];
then
multi_func
else
echo "exiting"
exit
fi
The idea here is - if the script is invoked with ./script.sh input, if will run if the input starts with name1 or name2, using single_func. If the input provided doesn't start with name1 or name2, and is a file containing a list of items, elif will run (reason for -f) using multi_fuc, which is just single_func running with xarg on the provided file.
The 'single_func' component runs on the command line fine on its own (command "input"), and the 'multi_func' component runs fine with a test file (xargs < testfile.txt -n 1 ./single_func.sh). But when I put them together as above and try to run them together, only the first 'if' part works correctly. When provided with a file or some nonsense line not containing name1 or name2, the script simply exits without returning anything.
For the curious, I'm running entrez direct commands within the single_func block.
What am I doing wrong?
You need to write:
if [[ "$input" = name1* || "$input" = name2* ]]; then
Otherwise, the right-hand side of your || tests whether name2* is a non-empty string, which it always unconditionally is, making the statement always true.
If you don't want to repeat yourself (and your real use case is complex enough you can't just change it to if [[ "$input" = name[12]* ]]), use a case statement instead:
case $input in
name1*|name2*) echo "Either name1 or name2 prefix found";;
*) echo "Neither prefix found";;
esac

What does [[ $(echo ${lines} | grep \'_SUCCESS\') ]] mean?

I came across this bash command and not able to interpret as it always print NO which is in else part.
if [[ $(echo ${lines} | grep \'_SUCCESS\') ]] ; then echo \'Y\'; else echo \'N\'; fi;
exit 0
I have _SUCCESS file ins
[[ ... ]] is a bash construct that will transform the truth value of the expression within into a exit status code 0/1. if will execute the then branch if the exit status code is 0, and the else branch otherwise.
Within [[ ... ]], you still get command substitution, so echo ${lines} | grep \'_SUCCESS\' will be executed, and its output substituted into the command. These commands will output the line inside ${lines} that contains '_SUCCESS' (with single quotes!) if such is present, or nothing.
[[ ... ]] that contains a single string evaluates as true if string is non-empty, and false if empty.
Thus, the then branch will execute if ${files} contains '_SUCCESS'. If you are always getting a 'N' as output, it follows ${files} does not contain '_SUCCESS' (even if it maybe does contain _SUCCESS). If you want to look for _SUCCESS (without quotes), then grep _SUCCESS or equivalently grep '_SUCCESS' suffices.
This is a long way around of writing what sergio says in comments: grep will not only output (or not output) the lines, it will also signal with its exit status code whether something is found or not, and can thus directly be used as the if condition, without using [[ ... ]].

bash if "$1" == "0" is always false when running function for bash prompt

I have been struggling with this for a long time.
Trying to change colour as part of my prompt depending on the exit code of the last command.
I have reduced my prompt to a minimal example:
Red="\[\033[31m\]"
Green="\[\033[32m\]"
Reset="\[\033[0m\]"
statColour(){
if [[ "$1" == "0" ]]; then
echo -e "${Green} $1 "
else
echo -e "${Red} $1 "
fi
}
export PS1="$(statColour \$?)What Colour? $Reset"
And results in red always being used despite the fact the number is clearly 0 in the first instance.
I have tried [ and $1 -eq 0 with no success. Why isn't this working?
Try this:
Red="\033[35m"
Green="\033[32m"
Reset="\033[0m"
statColour(){
if [[ $1 = 0 ]]; then
echo -e "${Green} $1 "
else
echo -e "${Red} $1 "
fi
}
export PS1="\$(statColour \$?)What Colour? $Reset"
# ^
Color definitions changed
Call of statColour is now done every time, and not only once.
if [[ ]] optimized
For an explanation why you always take the false branch:
You are calling statColour with \$? as argument. The backslash ensures, that the $ is taken literally (and not as the start of a parameter expanson), so you have in effect the literal string $?. Since ? is a wildcard character, it is undergoing filename generation, i.e. the parameter is replaced by all files where the name is a $, followed by a single character. If there are no such files in your directory (which is probably the case), the string $? is passed literally to statColour.
Inside statColour, you wrote
[[ "$1" == "0" ]]
which means that you ask, whether the string $? is equal to the string 0. This is never the case, hence the comparision is always false.
For your problem, you could try this approach (not tested, so you may have to debug it a bit):
statColour() {
# Fetch the exit code of the last program
local last_exit_code=$?
if ((last_exit_code == 0)) # Numeric comparision
then
.....
else
...
fi
# Preserve the exit code
return $last_exit_code
}
and set the prompt as
PS1='$(statColour) '"$Reset"
The single quotes ensure that statColour is evaluated dynamically, while $Reset is in double quotes since it is OK to evaluate it statically.

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

Resources