Bash: two loops inside while loop - bash

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.

Related

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!

Multiple variables in a CASE statement-BASH

st file
failed" "aa" "2018-04-03T17:43:38Z" "2018-04-03T18:43:38Z"
I have code which reads line into varaible and compares it with differenr values ("succeded","failed","aborted") and writes appropriate output.
while read -r status name startdate enddate; do
case "$status" in
"\"aborted\"")
echo "Job " $name "aborted"
;;
"\"failed\"")
echo "Job " $name "failed" " In project Demo"
;;
"\"succeeded\"")
echo "Job " $name "success"
;;
#*) echo 0 + $name
esac
exit 0
done<st.txt
Now want to add another condition in existing case statement: compare two dates and if difference between them is greather than 5 minutes and status "aborted" print
"aborted-long running", if time difference is less than 5 minutes and status "aborted" then just print "aborted" for other conditions no need to change anything
i imported another variable elapsed which will calculate difference between start and end date
is it possible to put this variable (together with $status in case statement)
i introduced elapsed variable in code but don't know how to integrate with existing case
while read -r status name startdate enddate; do
startdate=${startdate//\"/}
stime=$(date -d "${startdate/T/ }" +%s)
enddate=${enddate//\"/}
etime=$(date -d "${enddate/T/ }" +%s)
let elapsed=etime-stime
case "$status" in
"\"aborted\"")
echo "Job " $name "aborted"
;;
"\"failed\"")
echo "Job " $name "failed"
;;
"\"succeeded\"")
echo "Job " $name "success"
;;
*) echo 0 + $name
esac
exit 0
done<st.txt
Just add some more code to the aborted case branch:
case "$status" in
'"aborted"')
[[ $elapsed -gt 300 ]] && extra="-long running" || extra=""
echo "Job $name aborted $extra"
;;
'"failed"')
echo "Job $name failed"
;;
'"succeeded"')
echo "Job $name success"
;;
*) echo "0 + $name" ;;
esac
I would encourage you to use indentation on your code.
Also, quote your variables
Multiple IF statements: i thought it's more complicated
if [ "$status" == "\"aborted\"" ] && [ "$elapsed" -gt 300 ]; then echo "Long Running Process-Demo"
elif [ "$status" == "\"aborted\"" ] && [ "$elapsed" -lt 300 ]; then echo "Aborted-Demo"
elif [ "$status" == "\"failed\"" ]; then echo "Job" $name "failed-Demo"
elif [ "$status" == "\"succeeded\"" ]; then echo "Job" $name "Succeded"
fi

Infinite while loop in Korn 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.

Updating sub-menus variables

I have a problem with this bash script:
#!/bin/bash
G=100
echo $G
main_menu()
{
while :
do
clear
echo "Select from menu"
echo "[1] Press 1 to show savings"
echo "[2] Press 2 to withdraw savings"
echo "[3] Press 3 to exit"
echo "Enter your menu choice [1-3]: \c "
read -r m_menu
case "$m_menu" in
1) option_1;;
2) option_2;;
3) exit 0;;
*) echo "\nERROR: Please select a valid menu choice";
echo "Press ENTER To Continue..." ; read ;;
esac
done
}
option_1()
{
clear
echo "Your balance is $G"
echo "\nPress ENTER to return to menu..."
read
return
}
option_2()
{
clear
echo "Withdraw savings"
read -rp "Enter amount to withdraw: " num
if [ $num -le $G ]; then
answer=$(echo $(( G - num )))
echo "Your new balance is: $answer"
echo "$answer" | tee "$G"
elif [ $num -gt $G ]; then
echo "No: not eough money in your balance"
fi
read
return
}
main_menu
The problem is the following: how do I make sure that once I withdraw savings, my saving count will be updated? Because if I withdraw $90, obviously when I return to the balance it should be $10 and yet this doesn't work for me (it still says saving balance is $100)
What can I do?
Thank you (sorry for my poor English)
You have to update G in your code.
if [ $num -le $G ]; then
G=$(( G - num ))
echo "Your new balance is: $G"

Linux Shell-Script code is not executing?

This code is not running it shows error:- whl_case_lop1: 25: whl_case_lop1: Syntax error: "elif" unexpected (expecting ";;")
#if [ -z $1 ]
#then
#rental=****unknown item****
#elif [ -n $1 ]
#then
#rental=$1
#else [ -n $2 ]
#then
#rent=$2
#fi
echo "1. Vehicle_on_rent"
echo "2. Living_house"
echo -n "choose option [1 or 2]? "
read cate;
if [ $cate -eq 1 ];
then
echo "Enter your vehicle type"
read rental
case $rental in
"car") echo "for $rental in 20/km";;
"van") echo "for $rental in 15/km";;
"jeep") echo "for $rental in 10/km";;
"bike") echo "for $rental in 5/km";;
*) echo "We can't find $rental for your vehicle"
elif [ $cate -eq 2 ];
then
echo "Enter your Room requirement"
read rent
case $rent in
"1BHK") echo "for $rent is 10k";;
"2BHK") echo "for $rent is 15k";;
"3BHK") echo "for $rent is 20k";;
*) echo "we can't find $rent for your Requirement"
else
echo "Please check your requirements! Maybe you choose a wrong option"
fi
You need a ;; in your case's *) branch, and the case statement ends with the esac keyword: https://www.gnu.org/software/bash/manual/bashref.html#Conditional-Constructs
You might want to use the select statement, to restrict the user's responses:
PS3="Enter your vehicle type: "
select rental in car van jeep bike; do
case $rental in
car) echo "for $rental in 20/km"; break ;;
van) echo "for $rental in 15/km"; break ;;
jeep) echo "for $rental in 10/km"; break ;;
bike) echo "for $rental in 5/km"; break ;;
*) echo "We can't find $rental for your vehicle" ;;
esac
done
Indent your code to aid maintainability.

Resources