I am working on a script to back up my CouchPotatoServer, but I'm having problems.
Here is the code I am having problems with:
select OPTION in Backup Restore Finish; do
echo "You choose $OPTION CouchPotatoServer settings";
case $OPTION in
Backup)
echo "Backing up settings file to $CPBPATH:";
cp $CPSPATH/settings.conf $CPBPATH/settings-"$(date +%Y%m%d-%H%M)".bak ;
echo "Done!"
break
;;
Restore)
echo "Please choose a backup to restore settings" ;
AVAILABLEFILES="($(find $CPBPATH -maxdepth 1 -print0 | xargs -0))"
select FILE in $AVAILABLEFILES; do
cp "$FILE" $CPSPATH/settings.conf ;
echo "restored $FILE"
break
;;
done
The problem is that after the user chooses one option and the code is executed, it keeps waiting for a new selection, but I want it to exit. How can I do that?
break exits a loop, but you have nested loops and get stuck in the outer one. break actually takes an argument to specify how many enclosing loops you want to exit, so when you replace your break with break 2, you'll also exit the outer select loop.
Here is a little script to demonstrate the different break levels in select statements:
#!/bin/bash
PS3="Outer selection: "
select option1 in outer1 outer2 ; do
echo "option1 is $option1"
PS3="Inner selection: "
case "$option1" in
outer1)
select option2 in inner1 inner2; do
echo "option2 is $option2, issuing 'break'"
PS3="Outer selection: "
break
done
;;
outer2)
select option2 in inner3 inner4; do
echo "option2 is $option2, issuing 'break 2'"
break 2
done
;;
esac
done
PS3 is the prompt shown when using the select statement. As long as the outer option is outer1, you'll loop back to the outer select because only a single break is issued; if you select outer2, you'll exit the program with break 2.
Related
I have a bash script:
PS3='Please enter your choice: '
options=("1" "2" "3" "4" "Quit")
select opt in "${options[#]}"
do
case $opt in
"1")
echo "Set configuration"
break
;;
"2")
echo "Setting configuration and execution Install"
break
;;
"3")
echo "Setting configuration and execution Unlink"
break
;;
"4")
echo "Running tests"
break
;;
"Quit")
break
;;
*) echo "Selected option '$REPLY' couldn't be find in the list of options";;
esac
done
I have 2 questions:
How can I run this script with predefined option? (For example, I want to execute this script with already selected 1st option)
Is it possible to reuse one option in another option? (For example my 1st option just setting config and my 2nd option also setting the same config and after that execute install, can they be written like if option 2 selected execute 1st option and then 2nd?)
And if something written too badly, I'm open to suggestions =)
How can I run this script with predefined option? (For example, I want
to execute this script with already selected 1st option)
It's a bit ugly with select, move all case logic out from do ... done cycle, make your script take args and rearrange it like this:
#!/bin/bash
PS3='Please enter your choice: '
options=("1" "2" "3" "4" "Quit")
[[ $1 ]] && opt=$1 || select opt in "${options[#]}"; do break; done
case $opt in
"1") echo "Set configuration";;
"2") echo "Setting configuration and execution Install";;
"3") echo "Setting configuration and execution Unlink";;
"4") echo "Running tests";;
"Quit") :;;
*) echo "Selected option '$REPLY' couldn't be find in the list of options";;
esac
Is it possible to reuse one option in another option? (For example my
1st option just setting config and my 2nd option also setting the same
config and after that execute install, can they be written like if
option 2 selected execute 1st option and then 2nd?)
Turn the code in options into functions, this way you could easily reuse it
fun1(){ echo "Set configuration"; }
fun2(){ echo "Execution Install"; }
...
case $opt in
"1") fun1;;
"2") fun1; fun2;;
...
Also there are these operators for case: ;& and ;;&
man bash
...
Using ;& in place of ;; causes execution to continue with the list associated with the next set of patterns.
Using ;;& in place of ;; causes the shell
to test the next pattern list in the statement, if any, and execute any associated list on a successful match.
So if you want to make option 1 also run if option 2 selected this can be done like so:
case $opt in
"2") fun1;&
"1") fun1;;
...
But personally I found this method a bit tricky and hard to read.
If you put the select part in a function
main(){
select opt in "${options[#]}"
do
case $opt in
"1")
set_config # <--- an other funtion for option 1 to reuse it
break
;;
.
.
.
}
# set a default option
def_opt=1
# or use command line argument
def_opt="$1"
you can call main with predefined option '$def_opt' with yes
yes "$def_opt" | main
After digging into this and trying to do my best, I still need a little help to finish my script.
Running script without any parameters are now working perfect.
Passing options in that way (getopts :c:i:u:d:s:h:) leads me to an error message after executing command sh ./script.sh -c => Wrong argument 'c' provided, run sh ./scripts/collection.sh -h for help
Passing options in that way (getopts "ciudsh") => working perfect, but still if I use argument that wasn't passed (ex. x) it would lead to error: Wrong argument '' provided, run sh ./scripts/collection.sh -h for help or sometimes even to this Syntax error: "(" unexpected (expecting "fi")
Please see my full script below, unfortunately for security reasons I can't post the content of the functions itself.
I would appreciate any help on fixing style, errors or anything else.
Based on your advice and other answers on stackoverflow I came up to this:
#!/usr/bin/env bash
#Colors
BRed='\033[1;31m'
Green='\033[0;32m'
BCyan='\033[1;36m'
NC='\033[0m'
f1(){
...
}
f2(){
...
}
f3(){
...
}
f4(){
...
}
f5(){
...
}
Help(){
echo -e "${Green}====================================================================================================================${NC}"
echo "You may execute the commands by selecting a number from menu or pass it as argument, see examples below:"
echo ""
echo -e "${Green}sh $0 ${BCyan}-argument${NC} :To execute specific command"
echo -e "${Green}sh $0 ${NC} :To see menu with all available options"
echo ""
echo -e "${BCyan} -c ${NC}..."
echo -e "${BCyan} -i ${NC}..."
echo -e "${BCyan} -u ${NC}..."
echo -e "${BCyan} -d ${NC}..."
echo -e "${BCyan} -s ${NC}..."
echo -e "${BCyan} -h ${NC}..."
echo -e "${Green}====================================================================================================================${NC}"
exit 1;
}
if [ $# -eq 0 ]
then
PS3='Please enter your choice: '
options=("1" "2" "3" "4" "5" "Help" "Quit")
select opt in "${options[#]}"
do
case $opt in
"1")
f1;;
"2")
f1; f2;;
"3")
f1; f2;;
"4")
f3;;
"5")
f4;;
"Help")
Help;;
"Quit")
break;;
*) echo -e "${BRed}Selected option ${BCyan}'$REPLY'${NC} ${BRed}couldn't be find in the list of provided options${NC}"
break;;
esac
done
fi
while getopts :c:i:u:d:s:h: OPTION
do
case $OPTION in
c)
f1;;
i)
f1; f2;;
u)
f1; f3;;
d)
f4;;
s)
f5;;
h)
Help;;
*) echo -e "${BRed}Wrong argument ${BCyan}'$OPTARG'${NC} ${BRed}provided, run${NC} ${BCyan}sh $0 -h${NC} ${BRed}for help${NC}"
esac
done
Sorry in advance since i am new to Unix coding. I have a Bash shell script that generates 2 other subscripts. The main script implements a menu that gives a choice to the user in which script to generate.
I have two problems. The first one is how to make the script that the user selects to execute when he selects it, and the second how to implement input validation in my menu so when the user inputs something different that 1 and 2 to get an error message. This is my code so far:
#!/bin/bash
echo "Welcome to scriptGen."
echo "Please select a script to execute by choosing 1 or 2:"
scripts="bDir mMail"
select option in $scripts
do
echo "You have selected script $option to execute."
done
cat > bDir.sh <<EOF1
#!/bin/bash
#code
EOF1
chmod +x bDir.sh
cat >mMail.sh <<EOF2
#!/bin/bash
#code
EOF2
chmod +x mMail.sh
Thanks for your time!
So you'll probably want a case statement for your user to select their input.
Also, I'm not sure what you're doing with the dynamic writing of scripts, but you'll probably be better off with functions in your code.
Something like the following:
#!/bin/bash
main () {
echo "Welcome to scriptGen."
echo
echo "1: bDir"
echo "2: mMail"
echo -n "Please select a script to execute by choosing 1 or 2: "
read user_input
case $user_input in
1)
bdir
;;
2)
mmail
;;
*)
echo "Unrecognised option '$user_input'. Exiting..."
exit 1
;;
esac
}
bdir () {
echo "Running bDir"
}
mmail () {
echo "Running mmail"
}
main "$#"
Explanation:
main () {
...
}
Creating a function called main. It's a clear enough name to let the user know what function is going to get called first.
echo -n "Please select a script to execute by choosing 1 or 2: "
The -n removes the new-line at the end. This gives a nicer user experience when they're prompted.
read user_input
Read the user's input and store it in the variable user_input. The capture will finish when the user presses enter. However, this can be combined with other flags like -n 1 to capture just 1 character and continue without requiring the user to press enter.
case $user_input in
1)
bdir
;;
2)
mmail
;;
*)
echo "Unrecognised option '$user_input'. Exiting..."
exit 1
;;
esac
A case statement. Given the value of user_input, if it's 1, run the bdir function. If it's 2 run the mmail function. Otherwise, echo the warning and exit.
main "$#"
Run our main function. We use $# to ensure all of the variables passed into the script are also passed into the main function.
I am new to bash script. I want the user to input his choice, but the while-do loop does not terminate. I tired all choices of read [option] but some of them does not work. read -a stop the loop but does not accept the input. I am using windows machine and I am not sure if I need to install some other lib or packages.
I tried: read -p, read -a, read -r , read
function user_input {
while true; do
echo "1. choice 1"
echo "2. choice 2"
read -p "What choice would you like: [1,2] " ANS
case $ANS in
'1')
environment="-A"
break;;
'2')
environment="-B"
break;;
*)
echo "Wrong input, try again";;
esac
done
}
Because you have a case nested in a while, you should use break 2 to break both. Otherwise define a variable CON=true and set it to false in the case.
I've got a simple Bash Menu here. Everything seems to work great, except I can't figure out how to get back to the Main Menu from my SubMenu. Upon executing the script, I select "3" to go into the "SubMenu" from there, I press "3" again to go back to the "Main Menu" but instead it just keeps showing me the "SubMenu" options.
I tried replacing "break" with "./menu.sh" which is the name of my script, which does seem to work, however, I'm certain that's probably not the most ideal way to handle this issue.
#!/bin/bash
clear
while true
do
clear
echo "######"
echo " Menu"
echo "######"
echo ""
PS3='Select an option: '
options=("Option1" "Option2" "SubMenu" "Exit")
select opt in "${options[#]}"
do
case $opt in
"Option1")
echo Option1
read -p ""
break
;;
"Option2")
echo Option2
read -p ""
break
;;
"SubMenu")
while true
do
clear
echo "#########"
echo " SubMenu"
echo "#########"
echo ""
PS3='Select an option: '
options=(
"SubMenu Option1"
"SubMenu Option2"
"Main Menu"
)
select opt2 in "${options[#]}"
do
case $opt2 in
"SubMenu Option1")
echo "Sub-Menu Option1"
read -p ""
break
;;
"SubMenu Option2")
echo "Sub-Menu Option2"
read -p ""
break
;;
"Main Menu")
"./menu"
;;
*) echo "invalid option";;
esac
done
done
;;
"Exit")
exit
;;
*) echo "invalid option"
;;
esac
done
done
First of all, break will break out of select. To leave the submenu while loop, you need to break 3 (to leave the inner select, the submenu while, and the outer select).
Type help break for more information.
A short note on your other approach, calling your script again: You should not launch a new child process with each iteration. Bash has an elegant way of "restarting" a program:
exec "$0"
This will execute the given executable (in this case $0 – the current script) in the current process. See the exec() system call for more information or type help exec to read the Bash specifics.
I've been experimenting with dialog and bash scripting lately. I created a dialog menu with three options: Users, Passwords, and Groups then also a quit option. I'm trying to run functions for each of these in a case statement but it always seems to fall through to the catchall last statement and output the echo "Something Else..." statement regardless of which option I choose instead of say running the echo statements in the related function, e.g. echo "Entering Users sub-menu"
I've tried running this with debugging on:
bash -x myscript
and I get the following output:
+ choice='Users
Error: Expected a box option.
Use --help to list options.'
+ '[' 'Users
Error: Expected a box option.
Use --help to list options.' '!=' ' Quit ']'
+ case $choice in
+ echo 'Something else. Where Am I?'
Something else. Where Am I?
+ exit 0
I'm still trying to figure out what "Expected a box option" means which sounds like its related to dialog but I'm also wondering if there's something broken with my if statement or case statement in bash.
the code to my script:
#!/bin/bash
function quit {
exit 0
}
function menu {
choice=$(dialog --backtitle "Rob Graves's Bash Scripting" \
--title "Main Menu" --menu "Choose one" 30 50 4 "Users" \
"- Do something with users" "Passwords"\
"- Do stuff with passwords" "Groups" "- Do things with groups" \
"Quit" "- Exit to desktop" --clear --nocancel 3>&1 1>&2 2>&3)
if [ "$choice" != "Quit" ]; then
case $choice in
Users)
users #calls users function
;;
Passwords)
passwords #calls passwords function
;;
Groups)
groups #calls groups function
;;
*)
echo "Something else. Where Am I?"
;;
esac
else
echo "Quitting..."
quit
fi
}
function users {
echo "Entering Users sub-menu"
}
function passwords {
echo "Entering Passwords sub-menu "
}
function groups {
echo "Entering Groups sub-menu"
menu
exit 0
Your immediate options seems to be that dialog command does not like the options --clear and --nocancel options at the end as you have mentioned. Re-ordering it seems to work fine as expected
choice=$(dialog --backtitle "Rob Graves's Bash Scripting" \
--title "Main Menu" \
--clear \
--nocancel \
--menu "Choose one" 30 50 4 \
"Users" "- Do something with users" \
"Passwords" "- Do stuff with passwords" \
"Groups" "- Do things with groups" \
"Quit" "- Exit to desktop" 3>&1 1>&2 2>&3)
Also it would be good idea to always quote your case option strings as below
case "$choice" in
"Users")
users #calls users function
;;
"Passwords")
passwords #calls passwords function
;;
"Groups")
groups #calls groups function
;;
*)
echo "Something else. Where Am I?"
esac
Also remember you can also add options for "Users" as "1 - Users" in both the dialog menu and in the case menu as below.
and in case statement as
case "$choice" in
"1 - Users")
Also note that the commands users(1) and groups(1) are standard commands available as part of GNU bash and using the same name for functions has a possibility of bringing an uncertainty. Always choose names that are unambiguous.
Remember to exit the script with a non-zero exit code on failure cases from the script. For e.g. on the default case above, remember to add an exit 1, so that it adds one other way of debug facility to look in, when the script exits abnormally, not running the expected flow of sequence.
*)
echo "Something else. Where Am I?"
exit 1
;;
When this is hit and when your script exits and doing echo $? would have shown the code returned.
Also drop the non-standard function keyword from the function definition in the script. As long as the script runs in bash shell it should be fine, on a pure POSIX only shell, the keyword might not be recognized.
Also you should use #!/usr/bin/env bash for portability: different *nixes put bash in different places, and using /usr/bin/env is a workaround to run the first bash found on the PATH.