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

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

Related

Syntax for OR condition in bash while loop

I tried many different combinations with brakets, quotation marks, ||, -o, but the only way the script works without infinit loop is without the OR in while loop comparison, like this: while [ $name != Jorge ]] ; do ... This is an example of an script that I want to run:
#!/bin/bash
echo "What is my name: "
read name
while [ $name != "Jorge" ] || [ $name != "Eduardo" ] ; do
echo "Not. Try again: "
read name
done
echo "Well done!"
You should use && instead of || in the condition of while statement.
You are trying to read a name from stdin and if that name is "Jorge" or "Eduardo", you are done. when putting it in condition of while, you want to continue in the loop when the name if not "Jorge" and the name is not "Eduardo".
your current condition says that continue in the loop if the name is not "Jorge" or the name is not "Eduardo". And the name cannot be both at the same time.
Using regex:
while [[ ! "$name" =~ ^(Jorge|Eduardo)$ ]]; do
or Bash extented globbing:
shopt -s extglob
while [[ "$name" != #(Jorge|Eduardo) ]]; do

How to use contents of text file as input to shell script?

I'm tasked with writing a shell script that takes in a string of 6 letters and numbers and checks if they meet a certain criteria.
This is that script
FILE=$1
var=${#FILE}
if [ $var -gt 6 ] || [ $var -lt 6 ]
then
#echo $FILE "is not a valid NSID"
exit 1
else if ( echo $1 | egrep -q '^[A-Za-z]{3}\d{3}$' )
then
#echo $1 "is a valid NSID"
exit 0
else
#echo $1 "is not a valid NSID"
exit 1
fi
fi
It works. so that isn't where the problem is.
What I am trying to do is use a "wrapper" script to gather potential valid NSID's from a text file and call that script on the list of inputs. so that if I call that script within my wrapper script it will step through the text file I have given my wrapper and check if each line is valid.
FILE=$1
YES= more $FILE
if ( exec /path/to/file/is_nsid.sh $YES -eq 0 )
then
echo $FILE "is a valid NSID"
else
echo $FILE "is not a valid NSID"
fi
so if I called it with a text file called file1.txt which contained
yes123
yess12
ye1243
it would output whether each was valid or not.
The line
YES= more $FILE
Sets YES in the environment passed to the command more $FILE. That's probably not what you intended.
The line
if ( exec /path/to/file/is_nsid.sh $YES -eq 0 )
starts a subshell to execute exec /path/to/file/is_nsid.sh $YES -eq 0. (That's what the parentheses do.) exec then replaces the subshell with a process which executes
/path/to/file/is_nsid.sh $YES -eq 0
which in turn runs the script at is_nsid.sh, passing it two or three command line arguments:
the value of $YES. This could be several arguments if the value of the shell variable includes whitespace or a glob symbol, but in this case it is more likely to be nothing since $YES has not been defined.
-eq
0
Since your script only examines its first argument, that's probably equivalent to
/path/to/file/is_nsid.sh -eq
That will, presumably, terminate with a failure status code, and since the subshell has been replaced with the script execution, that will also be the return status of the subshell. (Without exec, there would be essentially no difference; the subshell's return status is that of the last command executed in the subshell. Without either the parentheses or the exec, the result would also be the same. So you could have just written if /path/to/file/is_nsid.sh $YES -eq 0 and it would produce the same incorrect result.)
What you presumably wanted to do was to read each line in the file whose name is passed as the first command-line argument to the script. You could do that as follows:
while read -r line; do
if /path/to/file/is_nsid.sh "$line"; then
echo "$line is a valid NSID"
else
echo "$line is not a valid NSID"
fi
done < "$1"
You could simplify your is_nsid script considerably. The following is equivalent:
[ $#1 -eq 6 ] && echo "$1" | egrep -q '^[A-Za-z]{3}\d{3}$'
Note that \d is a Gnu extension to egrep and should not be relied on in portable code (which I assume this is trying to be). You should use [0-9] or [[:digit:]] instead.
The length check is actually unnecessary since the regex can only match six-character lines. Personally, I'd leave it out and just use
echo "$1" | egrep -q '^[[:alpha:]]{3}[[:digit:]]{3}$'
I removed all the unnecessary if statements. If I had left them in, I would have changed else if ... then ... fi to simply elif ... then ... to avoid unnecessary if nesting.

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.

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 while loop with mixed condition

I'm not meaning multiple conditions, well it's also but mixed from that:
while [[ read line ] && [ "$GoodURL" == "false" ]]
What is the correct form for that? It's a while loop that runs on a text file line by line,
and I want to stop it with that $GoodURL type of boolean, please help.
Thanks.
while read line && [[ "$GoodURL" == "false" ]]
do
echo $line;
done
In case you want to read from a file/pipe, be sure to use indirection or you will get funny results (due the while loop executing in a subshell and not actually using the same environmen as the surrounding shell)
while read line && [[ "$GoodURL" == "false" ]]
do
echo $line;
done < input.txt

Resources