Syntax for OR condition in bash while loop - bash

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

Related

Bash script with multiline variable

Here is my code
vmname="$1"
EXCEPTLIST="desktop-01|desktop-02|desktop-03|desktop-04"
if [[ $vmname != #(${EXCEPTLIST}) ]]; then
echo "${vmname}"
else
echo "Its in the exceptlist"
fi
The above code works perfectly but my question is , the EXCEPTLIST can be a long line, say 100 server names. In that case its hard to put all that names in one line. In that situation is there any way to make the variable EXCEPTLIST to be a multiline variable ? something like as follows:
EXCEPTLIST="desktop-01|desktop-02|desktop-03| \n
desktop-04|desktop-05|desktop-06| \n
desktop-07|desktop-08"
I am not sure but was thinking of possibilities.
Apparently I would like to know the terminology of using #(${})- Is this called variable expansion or what ? Does anyone know the documentation/explain to me about how this works in bash. ?
One can declare an array if the data/string is long/large. Use IFS and printf for the format string, something like:
#!/usr/bin/env bash
exceptlist=(
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
)
pattern=$(IFS='|'; printf '#(%s)' "${exceptlist[*]}")
[[ "$vmname" != $pattern ]] && echo good
In that situation is there any way to make the variable EXCEPTLIST to be a multiline variable ?
With your given input/data an array is also a best option, something like:
exceptlist=(
'desktop-01|desktop-02|desktop-03'
'desktop-04|desktop-05|desktop-06'
'desktop-07|desktop-08'
)
Check what is the value of $pattern variable one way is:
declare -p pattern
Output:
declare -- pattern="#(desktop-01|desktop-02|desktop-03|desktop-04|desktop-05|desktop-06)"
Need to test/check if $vmname is an empty string too, since it will always be true.
On a side note, don't use all upper case variables for purely internal purposes.
The $(...) is called Command Substitution.
See LESS=+'/\ *Command Substitution' man bash
In addition to what was mentioned in the comments about pattern matching
See LESS=+/'(pattern-list)' man bash
See LESS=+/' *\[\[ expression' man bash
s there any way to make the variable EXCEPTLIST to be a multiline variable ?
I see no reason to use matching. Use a bash array and just compare.
exceptlist=(
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
)
is_in_list() {
local i
for i in "${#:2}"; do
if [[ "$1" = "$i" ]]; then
return 0
fi
done
return 1
}
if is_in_list "$vmname" "${EXCEPTLIST[#]}"; then
echo "is in exception list ${vmname}"
fi
#(${})- Is this called variable expansion or what ? Does anyone know the documentation/explain to me about how this works in bash. ?
${var} is a variable expansion.
#(...) are just characters # ( ).
From man bash in Compund commands:
[[ expression ]]
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, as if the extglob shell option were enabled. ...
From Pattern Matching in man bash:
#(pattern-list)
Matches one of the given patterns
[[ command receives the #(a|b|c) string and then matches the arguments.
There is absolutely no need to use Bash specific regex or arrays and loop for a match, if using grep for raw string on word boundary.
The exception list can be multi-line, it will work as well:
#!/usr/bin/sh
exceptlist='
desktop-01|desktop-02|desktop-03|
deskop-04|desktop-05|desktop-06|
desktop-07|deskop-08'
if printf %s "$exceptlist" | grep -qwF "$1"; then
printf '%s is in the exceptlist\n' "$1"
fi
I wouldn't bother with multiple lines of text. This is would be just fine:
EXCEPTLIST='desktop-01|desktop-02|desktop-03|'
EXCEPTLIST+='desktop-04|desktop-05|desktop-06|'
EXCEPTLIST+='desktop-07|desktop-08'
The #(...) construct is called extended globbing pattern and what it does is an extension of what you probably already know -- wildcards:
VAR='foobar'
if [[ "$VAR" == fo?b* ]]; then
echo "Yes!"
else
echo "No!"
fi
A quick walkthrough on extended globbing examples: https://www.linuxjournal.com/content/bash-extended-globbing
#!/bin/bash
set +o posix
shopt -s extglob
vmname=$1
EXCEPTLIST=(
desktop-01 desktop-02 desktop-03
...
)
if IFS='|' eval '[[ ${vmname} == #(${EXCEPTLIST[*]}) ]]'; then
...
Here's one way to load a multiline string into a variable:
fn() {
cat <<EOF
desktop-01|desktop-02|desktop-03|
desktop-04|desktop-05|desktop-06|
desktop-07|desktop-08
EOF
}
exceptlist="$(fn)"
echo $exceptlist
As to solving your specific problem, I can think of a variety of approaches.
Solution 1, since all the desktop has the same desktop-0 prefix and only differ in the last letter, we can make use of {,} or {..} expansion as follows:
vmname="$1"
found=0
for d in desktop-{01..08}
do
if [[ "$vmname" == $d ]]; then
echo "It's in the exceptlist"
found=1
break
fi
done
if (( !found )); then
echo "Not found"
fi
Solution 2, sometimes, it is good to provide a list in a maintainable clear text list. We can use a while loop and iterate through the list
vmname="$1"
found=0
while IFS= read -r d
do
if [[ "$vmname" == $d ]]; then
echo "It's in the exceptlist"
found=1
break
fi
done <<EOF
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
desktop-07
desktop-08
EOF
if (( !found )); then
echo "Not found"
fi
Solution 3, we can desktop the servers using regular expressions:
vmname="$1"
if [[ "$vmname" =~ ^desktop-0[1-8]$ ]]; then
echo "It's in the exceptlist"
else
echo "Not found"
fi
Solution 4, we populate an array, then iterate through an array:
vmname="$1"
exceptlist=()
exceptlist+=(desktop-01 desktop-02 desktop-03 deskop-04)
exceptlist+=(desktop-05 desktop-06 desktop-07 deskop-08)
found=0
for d in ${exceptlist[#]}
do
if [[ "$vmname" == "$d" ]]; then
echo "It's in the exceptlist"
found=1
break;
fi
done
if (( !found )); then
echo "Not found"
fi

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

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

Compare $1 with another string in bash

I've spent 2 hours with an if statement, that never works like I want:
#should return true
if [ "$1" == "355258054414904" ]; then
Here is the whole script:
#!/bin/bash
param=$1
INPUT=simu_900_imei_user_pass.csv
OLDIFS=$IFS
IFS=,
[ ! -f $INPUT ] && { echo "$INPUT ime not found"; exit 99; }
while read imei email pass
do
echo "First Parameter-IMEI: $1"
if [ "$1" == "355258054414904" ]; then
echo "GOOD"
fi
done < $INPUT
IFS=$OLDIFS
This is the output of the script:
First Parameter-IMEI: 355258054414904
First Parameter-IMEI: 355258054414904
First Parameter-IMEI: 355258054414904
I have seen a lot of pages about the subject, but I can't make it work :(
EDIT: I Join the content of csv for better understanding ! Tx for your help !
4790057be1803096,user1,pass1
355258054414904,juju,capp
4790057be1803096,user2,pass2
358854053154579,user3,pass3
The reason $1 does not match is because $1 means the first parameter given to the script on the command line, while you want it to match the first field read from the file. That value is in $imei.
You probably meant:
if [ "$imei" == "355258054414904" ]; then
echo "GOOD"
fi
Since it is inside the loop where you read input file line by line.
To check content of $1 use:
cat -vet <<< "$1"
UPDATE: To strip \r from $1 have this at top:
param=$(tr -d '\r' <<< "$1")
And then use "$param" in rest of your script.
To test string equality with [ you want to use a single '=' sign.

shell script if else loop not working

Given the following programme which reads in user input twice
function search_grep
{
if [ "$2" == "" ];then
for x in "${title[#]}"
do
value=$(echo $x | grep "$1")
if [ "$value" != "" ];then
echo "$value"
fi
done
elif [ "$1" == "" ];then
hello="123"
echo "$hello"
fi
}
echo -n "Enter title : "
read book_title
echo -n "Enter author : "
read author
title=(CatchMe HappyDay)
search_grep $book_title $author
it works as expected when i enter followed by HappyDay HOWEVER
When i enter foo followed by , I would expect console output to be
123
instead I am getting
Can someone explain to me , the programme is not executing the second elif loop though second input is
In both of your cases cases, the following:
search_grep $book_title $author
expands to a call with a single argument. Hence, the "then" clause is activated. The reason is that an unquoted argument consisting of whitespace expands to nothing and disappears. That is the way of bash.
If you want to pass search_grep two arguments, then you need to quote the variables:
search_grep "$book_title" "$author"
As shown here, you might try using = instead of ==
Or for an empty string comparison try -z

Resources