Bash If statement to compare array's variables - bash

Try to figure out how to apply conditions to two variables, one of them read from file and stored into array.
Seems that the problem is the second variable, the one stored into array.
#!/bin/bash
#CHECKTIME
#GET TIME
IFS=- read -r DAY HOUR MINUTE < <(date +%u-%H-%M)
echo $DAY
echo $HOUR
echo $MINUTE
arr=()
while IFS= read -r line; do
arr+=("$line")
done < myFile.txt
echo ${arr[0]}
echo ${arr[1]}
echo ${arr[2]}
if [ $DAY = $arr[0]]
then
echo "do event"
else
echo "don't do event"
fi
Thanks

The syntax for getting an element of an array is this:
${arr[0]}
Although that's not the only problem with your script. You could use ShellCheck to debug the rest. To start, [ $DAY = $arr[0]] is invalid.

You are mixing zsh and bash syntax here. You if statement would be valid under zsh (although in zsh it should be $arr[1] instead of $arr[0]). In bash, you can do either
if [ "$DAY" = "${arr[0]}" ]
or (if you don't like quoting)
if [[ $DAY == ${arr[0]} ]]

Related

Nesting If Condition Inside A While Loop

I'm reading the contents of a file and storing them in 2 variables then simultaneously want to compare it with an array using if statement. Code is given below
#!/bin/bash
# Define File
datafile=./regions-with-keys
# Create Nodes File
cat << EOF > $datafile
region1 key1
region2 key2
region3 key3
EOF
# User Input
clear;
echo -ne "PLEASE SELECT REGIONS(s) :\n\033[37;40m[Minimum 1 Region Required]\033[0m"
read -ra a
echo "${a[#]}"
# Reading Regions & Keys
for i in "${a[#]}"
do
while read -r $b $c; do
if [ "${a[#]}" -eq "$b" ]; then
echo "$b" "$c"
fi
done < $datafile
done;
it gives command not found for if statement when executed..
Aim of the code is to match the array indexes of userinput with $a from $datafile, if match is successful print
$b and $c
Try this Shellcheck-clean code:
#!/bin/bash -p
# Define File
datafile=./regions-with-keys
# Create Nodes File
cat <<EOF >"$datafile"
region1 key1
region2 key2
region3 key3
EOF
# User Input
clear
echo 'PLEASE SELECT REGIONS(s) :'
echo -ne '\e[37;40m[Minimum 1 Region Required]\e[0m'
read -ra input_regions
declare -p input_regions
# Reading Regions & Keys
for input_rgn in "${input_regions[#]}" ; do
while read -r data_rgn key ; do
if [[ $data_rgn == "$input_rgn" ]] ; then
printf '%s %s\n' "$data_rgn" "$key"
fi
done <"$datafile"
done
Significant changes from the code in the question are:
Use meaningful variable names.
Use declare -p input_regions to print the contents of the array in an unambiguous way.
Use varname instead of $varname as arguments to read. That fixes a serious bug in the original code.
Use printf instead of echo for printing variable values. See Why is printf better than echo?.
Used [[ ... == ...]] instead of [ ... -eq ... ] for comparing the region names. [[ ... ]] is more powerful than [ ... ]. See Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash?. Also, -eq is for comparing integers and == (or, equivalently, =) is for comparing strings.
Did various cleanups (removed some blank lines, removed unnecessary semicolons, ...).
The new code is Shellcheck-clean. Shellcheck identified several problems with the original code.
If you want to report incorrect input regions, try replacing the "Reading Regions & Keys" code with this:
for input_rgn in "${input_regions[#]}" ; do
# Find the key corresponding to $input_rgn
key=
while read -r data_rgn data_key ; do
[[ $data_rgn == "$input_rgn" ]] && key=$data_key && break
done <"$datafile"
if [[ -n $key ]] ; then
printf '%s %s\n' "$input_rgn" "$key"
else
printf "error: region '%s' not found\\n" "$input_rgn" >&2
fi
done

Using a pipe inside a for loop. How do I preserve the value of the variable inside the for loop

I've been stuck for quite some time with the following code. It works but the variable loses the value that was set during the iterations.
I have the following code
mistakes=0
entered_chars=()
word_length=0
answer=""
answer_guess=""
checkIfLetterInsideWord(){
exists=0
letter=$2
word_array=`echo $1 | grep -o . `;
for (( i=1; i <= $word_length; i++))
do
if [[ "${1:$i-1:1}" = ${letter} ]]; then
exists=1
answer_guess=$(echo $answer_guess | sed "s/-/${letter}/{i}" )
fi
done
echo $exists
}
askUserInput(){
answer=$answer
echo $answer
echo "Please type a letter"
read user_input
if [ ! -z $user_input ]; then
user_input=$(echo $user_input | tr '[:upper:]' '[:lower:]')
if [ $(checkIfAlreadyEntered "$user_input") -eq 0 ]; then
if [ $(checkIfLetterInsideWord $answer $user_input) -eq 0 ]; then
mistakes=$((mistakes + 1)); fi
echo "Current mistake count; $mistakes "
entered_chars+=($user_input)
else
echo "Char has already been entered"
fi
else
echo "You haven't entered any input!"
fi
}
guessTheWord() {
answer=$OPTARG
word_length=$(printf $answer | wc -m)
temp=$(echo $answer | sed 's/\(.\)/\1 /g')
array=($temp)
echo "The chosen word is $word_length long"
gameOngoing=true
for(( i=1; i<=$word_length; i++)) do
answer_guess="$answer_guess-"
done
while $gameOngoing
do
echo $answer_guess
askUserInput $answer
done
}
I want to preserve the value of the variable answer_guess. I understand that it loses the value because of the usage of a pipeline inside the loop but I don't know to approach this problem.
The problem has nothing do to with the pipe. Rather, it is that you call checkIfLetterInsideWord inside a command-substitution ($(...)). Command substitution executes in a subshell so environment changes in the function will not persist.
It would be better to rewrite checkIfLetterInsideWord so that it returns an exit status. Something like:
if [[ $exists ]]; then
return 0 # Success
else
return 1 # Failure
end
Then you could simply call it without worrying about a subshell:
if checkIfLetterInsideWord "$answer" "$user_input"; then
# letter is in word
else
# letter is not in word
fi
There are other issues with the code. I've limited this answer to the question about preserving the value of variables.
answer_guess=$(echo $answer_guess | sed "s/-/${letter}/{i}" )
replace the - with .
so your code becomes
answer_guess=$(echo $answer_guess | sed "s/./${letter}/{i}" )

How to break infinite loop in this script

I am doing something interesting with bash
I wrote script below:
#!/bin/bash
while :
do
if [ -s /tmp/file.txt ]; then
for line in $(cat /tmp/file.txt)
do
echo $line
#May be some commands here
done
fi
done
and the content of my file.txt is:
1 True
2 Flase
How can I say the script if command cat /tmp/file.txt is finished (I mean all lines are read) and also echo $line and other commands are finished then break the infinitive while : loop?
Thank you
Use break.
#!/bin/bash
while :
do
if [ -s /tmp/file.txt ]; then
for line in $(cat /tmp/file.txt)
do
echo $line
#May be some commands here
done
break
fi
done
Although it would be simpler and more proper with:
#!/bin/bash
for (( ;; )); do
if [[ -s /tmp/file.txt ]]; then
# Never use `for X in $()` when reading output/input. Using word splitting
# method for it could be a bad idea in many ways. One is it's dependent with
# IFS. Second is that glob patterns like '*' could be expanded and you'd
# produce filenames instead.
while read line; do
# Place variables between quotes or else it would be subject to Word
# Splitting and unexpected output format could be made.
echo "$line"
done < /tmp/file.txt
break
fi
done
On another note, do you really need the outer loop? This time you don't need to use break.
#!/bin/bash
if [[ -s /tmp/file.txt ]]; then
while read line; do
echo "$line"
done < /tmp/file.txt
fi

Bash string compare cannot match

I want to filter some collections in mongodb to export. But the string compare seems incorrect.
$1 in my case is localhost:17017/mydb
shop is one of the collections in mongodb, but $i == 'shop' never succeed.
#!/bin/bash
colls=`mongo $1 --eval 'db.getCollectionNames();' | tail -1`
IFS=',' read -ra ADDR <<< $colls
for i in "${ADDR[#]}"
do
if [[ $i == 'shop' ]]
then
echo $i
fi
done
Or is there any other methods to export specified collections from mongodb?
Try to echo the values you got and see perhaps how you should actually use the patterns. Also please quote your variables properly. It's also better to use $() over backticks:
#!/bin/bash
colls=$(mongo "$1" --eval 'db.getCollectionNames();' | tail -1)
echo "colls: $colls"
IFS=',' read -ra ADDR <<< "$colls"
echo "colls count: ${#ADDR[#]}"
for i in "${ADDR[#]}"
do
echo "Trying |$i|."
if [[ $i == 'shop' ]]
then
echo "$i"
fi
done

Change variables in bash

How do I change this var ?
max=0;
min=20000000;
cat |while read
do
read a
if [[ $a -gt $max ]]
then
max=a`
fi
`if [[ $a -lt $min ]]
then
min=a
fi
done
echo $max
echo $min
My min and max are still the same, 0 and 2000000. Can anybody help me with this ? I have no idea.
The (main) problem with your script is that setting min and max happens in a subshell, not your main shell. So the changes aren't visible after the pipeline is done.
Another one is that you're calling read twice - this might be intended if you want to skip every other line, but that's a bit unusual.
The last one is that min=a sets min to a, literally. You want to set it to $a.
Using process substitution to get rid of the first problem, removing the (possibly) un-necessary second read, and fixing the assignments, your code should look like:
max=0
min=20000000
while read a
do
if [[ $a -gt $max ]]
then
max=$a
fi
if [[ $a -lt $min ]]
then
min=$a
fi
done < <(cat) # careful with the syntax
echo $max
echo $min

Resources