Infinite while loop in Korn Shell - shell

I have the following code :
#!/bin/ksh
echo "1) Option 1"
echo "2) Option 2"
echo "3) Option 3"
echo "4) Option 4"
opt=0
while [ x$opt = "x" ] || [ $opt != "1" ] || [ $opt != "2" ] || [ $opt != "3" ] || [ $opt != "4" ]; do
printf "Enter [1|2|3|4] : "
read -r opt
done
echo "Option selected : $opt
The while [ x$opt = "x" ] means "while $opt is empty" in case you wonder.
Here's the output :
Will:/home/will> ./script.ksh
Enter [1|2|3|4] :
Enter [1|2|3|4] : jde
Enter [1|2|3|4] : 1
Enter [1|2|3|4] : 2
Enter [1|2|3|4] : 3
Enter [1|2|3|4] : 4
Enter [1|2|3|4] :
So when typing nothing or something unexpected it loops, OK.
But why does it still loops when 1/2/3 and 4 are typed when it should prompt the last echo message ?
EDIT :
I have added a echo $opt in the while loop, and it prints me this :
Enter [1/2/3/4] : d
d
Enter [1/2/3/4] : 1
1
Enter [1/2/3/4] :
So $opt received the user input as it should, but the while still never breaks.

You may want to avoid multiple if conditions using much cleaner case expressions:
#!/bin/ksh
echo "1) Option 1"
echo "2) Option 2"
echo "3) Option 3"
echo "4) Option 4"
while true
do
case $opt in
1|2|3|4)
break ;;
*)
printf "Enter [1|2|3|4] : "
read -r opt ;;
esac
done
echo "Option selected : $opt"
About your problem, you don't need || between conditions, you actually need to use && to check for all negative conditions:
#!/bin/ksh
echo "1) Option 1"
echo "2) Option 2"
echo "3) Option 3"
echo "4) Option 4"
opt=
while [ "x$opt" = "x" ] && [ "$opt" != "1" ] && [ "$opt" != "2" ] && [ "$opt" != "3" ] && [ "$opt" != "4" ]; do
printf "Enter [1|2|3|4] : "
read -r opt
done
echo "Option selected : $opt"
Also it is important to quote $opt inside [ ... ] as shown in my answer.

[ $opt != "1" ] || [ $opt != "2" ] will always be true, whatever value $opt has.
Therefore, the behaviour is normal.

Related

Bash: default boolean value in getopts

I want to include a default option in my script where if the user uses this option, set the flag to true or it should be false by default. It seems that the script is not accepting false or true as boolean value. How can I make it boolean?
flag=
instructions() {
echo " -a File name" >&2
echo " -f optional boolean" flag=${flag:-false}
}
while getopts ":a:fi" option; do
case "$option" in
a ) file=$OPTARG;;
f ) flag=true;;
u )
instructions
;;
\?)
echo "Not valid -$OPTARG" >&2
instructions
;;
: ) echo "args required";;
esac
done
if [[ "$flag" != true || "$flag" != false ]]; then
echo "Not a boolean value"
fi
Check this, I made some fixes to your script (commented in the code) along with a proper formatting.
#!/bin/bash
# Set the default value of the flag variable
flag=false
instructions() {
echo "Usage: $0 [ -a FILE ] [ -f ]" >&2
echo " -a File name" >&2
echo " -f optional boolean flag=${flag:-false}" >&2
}
# If the script must be executed with options, this checks if the number of arguments
# provided to the script is greater than 0
if [ $# -eq 0 ]; then
instructions
exit 1
fi
while getopts ":a:fi" option; do
case "${option}" in
a )
file="$OPTARG"
;;
f )
flag=true
;;
i ) # "u" is not a valid option
instructions
exit 0
;;
\?)
echo "Option '-$OPTARG' is not a valid option." >&2
instructions
exit 1
;;
: )
echo "Option '-$OPTARG' needs an argument." >&2
instructions
exit 1
;;
esac
done
# Since a variable can't have 2 values assigned at the same time,
# you should use && (and) instead of || (or)
if [[ "$flag" != true ]] && [[ "$flag" != false ]]; then
echo "Not a boolean value"
fi
exit 0

Bash script to chose based on user input

Problem:
I need to make this bash script to choose based on the user inputs. Example how can i add choices? such that user just select from 1 to 3 and that is set in variable CLUSTER_NAME.
choices are test.com, try.com and me.com
Script
#!/bin/bash
sops_ops() {
sops --version
if [ "$?" -eq "0" ]; then
echo "proceed sops ops"
else
echo "check sops binary"
fi
read -p 'Enter cluster_NAME: = ' CLUSTER_NAME
test_environment="test.com"
test1_environment="test1.com"
test2_environment="test2.com"
case "${$CLUSTER_NAME}" in
prod.$test_environment) ;;
dev.$test1_environment) ;;
stage.$test2_environment) ;;
test.$test_environment) ;;
*) echo "Invalid option: ${CLUSTER_NAME}" 1>&2 && exit 1 ;;
if [ $CLUSTER_NAME = test.$test_env ];then
printf "got cluster $CLUSTER_NAME"
elif [ $CLUSTER_NAME = "test.test.com" ];then
printf "got dev cluster $CLUSTER_NAME"
echo "not found cluster"
else
echo "Environment not available"
fi
}
sops_ops
Question:
How do I do that?
Any help is appreciated!

Return to previous commands in bash script?

I'm trying to implement a prev option in my bash script to go back to the previous "menu", as well as a way for the script to ask for user input again if no variable is set for $name.
Heres my bash script:
#!/bin/bash
#Menu() {
for (( ; ; ))
do
beginORload=
echo "Choose option:"
echo "1 - Begin"
echo "2 - Load"
read -p "?" beginORload
#}
#Begin() {
if [ "$beginORload" -eq "1" ]
then
clear
for (( ; ; ))
do
echo "Beginning. What is your name?"
read -p "?" name
#If "prev" specified, go back to #Menu()
if [ "$name" -eq "prev" ]
then
Menu
fi
#If nothing specified, return to name input
if [ -z ${name+x} ]
then
Begin
else
break
fi
echo "Hi $name !"
done
fi
done
In batch, I could simply do:
:menu
echo Choose option:
echo 1 - Begin
echo 2 - Load
[...]
:begin
[...]
if "%name%==prev" goto menu
if "%name%==" goto begin
the issue is I keep running into errors all over the place, and I can't figure out what to type to get it to work
im running Yosemite btw. Thankyou
Something like this is close to what you expect:
while [[ $answer -ne '3' ]];do
echo "Choose option:"
echo "1 - Begin"
echo "2 - Load"
echo "3 - Exit"
read -p "Enter Answer [1-2-3]:" answer
case "$answer" in
1) while [[ "$nm" == '' ]];do read -p "What is your Name:" nm;done # Keep asking for a name if the name is empty == ''
if [[ $nm == "prev" ]];then nm=""; else echo "Hello $nm" && break; fi # break command breaks the while wrapper loop
;;
2) echo 'Load' ;;
3) echo 'exiting...' ;; # Number 3 causes while to quit.
*) echo "invalid selection - try again";; # Selection out of 1-2-3 , menu reloaded
esac # case closing
done # while closing
echo "Bye Bye!"
As a general idea you can wrap up your case selection in a while loop which will break under certain circumstances (i.e If Option 3 is selected or if a valid name is given (not blank - not prev)
PS1: In bash you compare integers with -eq , -ne, etc but you compare strings with == or !=
PS2: Check the above code online here

Bash: two loops inside while loop

As a beginner I'm not sure if this is the best way to do multiple loops!! though it works well, please suggest me if there is an elegant way of doing this (I'm not restricted to bash but I'm not familiar with other languages)
#!/bin/bash
while :; do
read -n1 -e -p"Top Levels 1-5: " top_levels
if [ "$top_levels" == "1" ]; then
echo "top level 1"
elif [ "$top_levels" == "2" ]; then
while :; do
read -n1 -e -p"Sub Levels 1-5: " sub_levels
if [ "$sub_levels" == "1" ]; then
echo "sub level 1"
elif [ "$sub_levels" == "2" ]; then
echo "sub level 2"
elif [ "$sub_levels" == "3" ]; then
while :; do
read -n1 -e -p"Final Levels 1-5: " final_levels
if [ "$final_levels" == "1" ]; then
echo "Final level 1"
elif [ "$final_levels" == "2" ]; then
echo "Final level 2"
elif [ "$final_levels" == "3" ]; then
echo "Final level 3"
elif [ "$final_levels" == "4" ]; then
echo "Final level 4"
else
echo "bye"
break
fi
done
elif [ "$sub_levels" == "4" ]; then
echo "sub level 4"
else
echo "bye"
break
fi
done
elif [ "$top_levels" == "3" ]; then
echo "top level 3"
elif [ "$top_levels" == "4" ]; then
echo "top level 4"
else
echo "bye"
exit
fi
done
I'd write it like this
#!/bin/bash
process_final_levels() {
while :; do
read -n1 -e -p"### Final Levels 1-5: " final_levels
case $final_levels in
1) echo "Final level 1";;
2) echo "Final level 2";;
3) echo "Final level 3";;
4) echo "Final level 4";;
*) echo "returning to sub levels"
break;;
esac
done
}
process_sub_levels() {
while :; do
read -n1 -e -p"# Sub Levels 1-5: " sub_levels
case "$sub_levels" in
1) echo "sub level 1" ;;
2) echo "sub level 2" ;;
3) process_final_levels;;
4) echo "sub level 4" ;;
*) echo "returning to top levels"
break;;
esac
done
}
while :; do
# get top levels to work with
read -n1 -e -p"Top Levels 1-5: " top_levels
case "$top_levels" in
1) echo "top level 1" ;;
2) process_sub_levels ;;
3) echo "top level 3" ;;
4) echo "top level 4" ;;
*) echo "Bye"
exit;;
esac
done
I changed the prompt and exiting strings to be more intuitive as to what level you are working on. I didn't add comments, b/c I have no idea what's this for. I functionalized, b/c any code that needs to be modified, tested, or maintained should be functionalized.

Bash not sourcing code as expected

I have these two scripts, configScript.shand genScript.sh. The first one works just the way I want it to work. It adds the correct values into options.sh and echo the right message. However, I want genScript.sh to accept the current argument in options.sh and output the correct echo. As it is now when I run genScript.sh it returns null and I can't figure out why.
#!/bin/bash -x
#configScript.sh
func()
{
echo "
Choose
1 - Option 1
2 - Option 2
"
echo -n " Enter selection: "
read select
case $select in
1 )
echo " Option 1 chosen"
. ./genScript.sh one
cat << EOF >options.sh
OPTION=$OPTION
EOF
;;
2 )
echo " Option 2 chosen"
. ./genScript.sh two
cat << EOF >options.sh
OPTION=$OPTION
EOF
;;
esac
}
func
#!/bin/bash -x
#genScript.sh
. options.sh
OPTION=$1
func2()
{
if [ "$OPTION" == one ] ; then
echo "Option one"
elif [ "$OPTION" == two ] ; then
echo "Option two"
else
echo "null"
fi
}
func2
I managed to get genScript.sh to work the way I want by removing OPTION=$1. When I do that genScript.sh will accept the value inside options.sh and will output the right echo . BUT when I remove OPTION=$1 configScript.sh stops working as it should, it doesn't update options.sh with a new value anymore.
The problem is with the way you want genScript to be called. I think you want to run genScript with command line argument and as-well as with sourcing from options.sh.
Below changes to genScript.sh would serve the purpose. It gives preference to command line when both command line and options.sh have values.
#!/bin/bash -x
#genScript.sh
OPTION=""
. options.sh
[ "$1" ] && OPTION=$1
func2()
{
if [ "$OPTION" == one ] ; then
echo "Option one"
elif [ "$OPTION" == two ] ; then
echo "Option two"
else
echo "null"
fi
}
func2
Just put quotes around "one" and "two" in the second script and in the first script where it generates options.sh and added default value $OPTION to the OPTION var in the second script so now it works.
#!/bin/bash -x
#configScript.sh
func()
{
echo "
Choose
1 - Option 1
2 - Option 2
"
echo -n " Enter selection: "
read select
case $select in
1 )
echo " Option 1 chosen"
. ./genScript.sh one
cat << EOF >options.sh
OPTION="$OPTION"
EOF
;;
2 )
echo " Option 2 chosen"
. ./genScript.sh two
cat << EOF >options.sh
OPTION="$OPTION"
EOF
;;
esac
}
func
#!/bin/bash -x
#genScript.sh
. options.sh
OPTION=${1-$OPTION}
func2()
{
if [ "$OPTION" == "one" ] ; then
echo "Option one"
elif [ "$OPTION" == "two" ] ; then
echo "Option two"
else
echo "null"
fi
}
func2
That is one of the most irritating problems. I don't know if you are using editor with syntax highlighting but you better be so you run easily over this type of issues.

Resources