Why is empty string changed into -n expression in bash - 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.

Related

Detecting when a string exists but doesn't start with - in bash

I am trying to make a bash program that saves results to a file with the name of the user's choosing if the program is supplied the --file argument followed by an option, in which the option should not start with a dash. So I used the following conditional:
if [[ -n $2 && !($2="[^-]") ]]
But that didn't work. It still saves the output to a file even if the second argument starts with a dash. I also tried using this:
1) if ! [[ -z $2 && ($2="[^-]") ]]
It also did as the previous one. What's the problem? Thanks in advance!
As a pattern match, this might look like:
[[ $2 ]] && [[ $2 != -* ]]
Note:
Moving && outside of [[ ]] isn't mandatory, but it is good form: It ensures that your code can be rewritten to work with the POSIX test command without either using obsolescent functionality (-a and -o) or needing to restructure.
Whitespace is mandatory. In !($2="[^-]"), neither the ! nor the ( and ) nor the = are parsed as separate operators.
= and != check for pattern matches, not regular expressions. The regular expression operator in [[ ]] is =~. Among the differences, anchors (^ to match at the beginning of a string, or $ to match at the end) are implicit in a pattern whereas they need to be explicit in a regex, and * has a very different meaning (* in a pattern means the same thing as .* in a regex).
The ^ in [^-] already negates the -, so by using ! in addition, you're making your code only match when there is a dash in the second argument.
To test this yourself:
$ check_args() { [[ $2 ]] && [[ $2 != -* ]]; echo $?; }
$ check_args one --two
1
$ check_args one two
0
$ check_args one
1

Bash comparison operator always true

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.

bash verbose string comparison slashes

I am comparing two strings in a bash script as follows:
x="hello"
y="hello"
if [[ "$x" != "$y" ]]; then
echo "different"
else
echo "same"
fi
This comparison works. When I execute the script with -x, the comparison still works, but it shows the output
+ x=hello
+ y=hello
+ [[ -n hello ]]
+ [[ hello != \h\e\l\l\o ]]
+ echo same
I'm curious why the right side of the string shows as\h\e\l\l\o and not hello
The simple explanation is for the same reason that the left-hand side doesn't have quotes around it.
-x is showing you an equivalent but not exact representation of what it ran. The right-hand side of = and != in [[ ... ]] is matched as a pattern.
From the manual:
When the == and != operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below under Pattern Matching. .... Any part of the pattern may be quoted to force it to be matched as a string.
The -x output, for some reason, chooses to use escaping instead quoting to disable pattern matching there.
When using =, ==, and != in [[, the right-side string can contain globs (*, ?, etc.).
The backslashes in your example aren't necessary, though they don't hurt. They are needed if the right-side string contains a possible wildcard character. For example:
$ set -x
$ [[ hi == 'hi*' ]]; echo $?
+ [[ hi == \h\i\* ]]
+ echo 1
1
$ [[ hi == hi* ]]; echo $?
+ [[ hi == hi* ]]
+ echo 0
0

check for string format in bash script

I am attempting to check for proper formatting at the start of a string in a bash script.
The expected format is like the below where the string must always begin with "ABCDEFG-" (exact letters and order) and the numbers would vary but be at least 3 digits. Everything after the 3rd digit is a do not care.
Expected start of string: "ABCDEFG-1234"
I am using the below code snippet.
[ $(echo "$str" | grep -E "ABCDEFG-[0-9][0-9][0-9]") ] && echo "yes"
str1 = "ABCDEFG-1234"
str2 = "ABCDEFG-1234 - Some more text"
When I use str1 in place of str everything works ok and yes is printed.
When I use str2 in place of str i get the below error
[: ABCDEFG-1234: unary operator expected
I am pretty new to working with bash scripts so any help would be appreciated.
If this is bash, you have no reason to use grep for this at all; the shell has built-in regular expression support.
re="ABCDEFG-[0-9][0-9][0-9]"
[[ $str =~ $re ]] && echo "yes"
That said, you might want your regex to be anchored if you want a match in the beginning rather than anywhere in the content:
re="^ABCDEFG-[0-9][0-9][0-9]"
[[ $str =~ $re ]] && echo "yes"
That said, this doesn't need to be an ERE at all -- a glob-style pattern match would also be adequate:
if [[ $str = ABCDEFG-[0-9][0-9][0-9]* ]]; then echo "yes"; fi
Try grep -E "ABCDEFG-[0-9][0-9][0-9].*"

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