Bash zenity list - issue with reading user's choice - bash

I have a script written in bash.
In one place I have a zenity list with a few options:
menu=("first option" "second option" "third option")
answer=`zenity --list --column=Menu "${menu[#]}" --height 170`
After that, when user choose first option I want to do something, when user choose second - something else, etc.
But idk how can I use the variable answer in If statement:
if [ option1 ]; then
...do something...
elif [ option2 ]; then
... do something else...
else
... do something different...
fi
When I chose first option and uses: echo "$answer", it printed "first option"
But when I tried:
if [ $answer = "first option" ]; then
....
fi
It didn't work.

You have to put $answer in "" to avoid word splitting:
#!/usr/bin/env bash
menu=("first option" "second option" "third option")
answer=`zenity --list --column=Menu "${menu[#]}" --height 170`
if [ "$answer" = "first option" ]; then
printf "First option has been chosen\n"
fi
Also, it's recommended to use $(...) instead of backticks. Replace:
answer=`zenity --list --column=Menu "${menu[#]}" --height 170`
with:
answer=$(zenity --list --column=Menu "${menu[#]}" --height 170)
EDIT:
I was also going to suggest using case here but it has already been suggested in the other answer.

Except for the quoting, if [ "$answer" = "first option" ] should work. You might want to use a case statement here:
case "$answer" in
"${menu[0]}")
echo do stuff for first
;;
"${menu[1]}")
echo do stuff for second
;;
"${menu[2]}")
echo do stuff for third
;;
esac

Related

Bash menu script with possibility to run with arguments

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

Making a shell script but the script is not taking any input

I am trying to make a shell script that works as an ordering system. I have started with the first steps but it does not take the input I am not sure of what I am doing wrong. I have attached an image of what end result should be. What is the next step I should take and what should I begin to research
#!/bin/bash
clear
echo "orderBeds"
read -p "Please Enter you choice (Quit/Order)" order
if [$order -e Order|order]
then
echo "Please enter you name?"
elif [$order -e Quit|quit]
then
exit
fi
done
I'll start giving some general advice.
1) [ is a command. That means you probably don't want to expand a variable just next to it without separating them with white spaces.
2) If you will check against more than one option, use the case construct. Apart from giving you the chance of a better structure, you'll be able to use globbing expressions as options to match against.
That said, let's rewrite your code:
#! /bin/bash
clear
echo "orderBeds"
read -p "Please Enter your choice (Quit/Order)" order
case "$order" in
[Oo]rder)
read -p "Please enter your name: " name
echo "$name placed an order."
break
;;
[Qq]uit)
exit
;;
esac
the -e flag is for numerical equivalency.
Here is a corrected bash script to get you started:
#!/bin/bash
clear
echo "orderBeds"
read -p "Please enter your choice (Quit/Order) " order
if [ $order == "order" ] || [ $order == "Order" ]
then
read -p "Please enter your name " name
echo "$name placed an order"
elif [ $order == "quit"] || [ $order == "Quit" ]
then
exit
fi
Note the use of == instead of -e and the separation of the or clauses.

Bash: While loop error

I'm trying to write a program where the program will ask you your name, confirm that you want this name, then check to see if you said yes or no. I have the "no" loop working, where it will ask for a name again, but I'm trying to a loop where if you type anything other than yes or no it will say "Please type yes or no." then ask if you want to confirm your name, then check the answer again. Heres what I have.
echo -e $WHITE"Name your$GREEN Hero$WHITE."
read HERO
clear
echo -e "Are you sure you want your$GREEN Hero$WHITE's name to be$GREEN "$HERO"$WHITE? y/n"
read ANSWER1
while [ $ANSWER1 = "no" ]; do
#
#
#
clear
echo -e $WHITE"Name your$GREEN Hero$WHITE."
read HERO
clear
echo -e "Are you sure you want your$GREEN Hero$WHITE's name to be$GREEN "$HERO"$WHITE? y/n"
read ANSWER1
done
while [ $ANSWER1 != "yes" ] || [ $ANSWER1 != "no" ]; do
#
#
#
clear
echo -e $WHITE"Please type yes or no."
sleep 1.5
clear
echo -e $WHITE"Are you sure you want your$GREEN Hero$WHITE's name to be$GREEN "$HERO"$WHITE?"
read ANSWER1
clear
done
clear
echo -e -n $WHITE"Loading"
sleep 1.5
echo -e -n "."
sleep 1.5
echo -e -n "."
sleep 1.5
echo -e -n "."
sleep 1.5
clear
echo -e "Done."
If I go through the "Please type yes or no." it will freeze at the sleep and not clear, therefore not displaying the echo and continuing the loop. Any help or suggestions would be greatly appreciated!
Here's the problem:
while [ $ANSWER1 != "yes" ] || [ $ANSWER1 != "no" ]; do
Every possible answer is different to at least one of them. There's no string that's equal to yes and no at the same time. You need && instead of ||.
An easier way to write a loop to get user's input is the select command:
while true; do
read -p "${white}Name your ${green}Hero$white: " hero
echo "Are you sure you want your ${green}Hero$white's name to be $green\"$hero\"$white? "
select ans in Yes No; do
case $ans in
Yes) break 2 ;; # break out of both select and while loops
No) break ;; # only break out of select loop
esac
done
done
echo "Your ${green}Hero$white's name is $green\"$hero\"$white."

Shell Script not reading input

I have written a script that backs up and restores files. I have a problem in that when the user enters '2' for a restore the program says that this is an invalid input, all other options work fine. I feel it is something small that I have missed but I cant fix it
Update and Restore Script
#!/bin/bash
ROOT="/Users/Rory/Documents"
ROOT_EXCLUDE="--exclude=/dev --exclude=/proc --exclude=/sys --exclude=/temp --exclude=/run --exlucde=/mnt --exlcude=/media --exlude=/backup2.tgz"
DESTIN="/Users/Rory/test/"
BACKUP="backup2.tgz"
CREATE="/dev /proc /sys /temp /run /mnt /media "
if [ "$USER" != "root" ]; then
echo "You are not the root user"
echo "To use backup please use: sudo backup"
exit
fi
clear
echo "************************************************"
echo "********* Backup Menu **************************"
echo "************************************************"
OPTIONS="BACKUP RESTORE DESTINATION EXIT"
LIST="1)BACKUP 2)RESTORE 3)DESTINATION 4)EXIT"
select opt in $OPTIONS; do
if [ "$opt" = "EXIT" ]; then
echo "GOODBYE!"
sleep 3
clear
exit
elif [ "$opt" = "BACKUP" ]; then
echo "BACKING UP FILES..."
sleep 2
tar cvpfz $DESTIN/backup.`date +%d%m%y_%k:%M`.tgz $ROOT $ROOT_EXCLUDE_DIRS
echo "BACKUP COMPLETE"
sleep 2
exit
elif [ "$opt" = "RESTORE" ]; then
echo "RESTOTING FILES..."
sleep 2
tar xvpfz $BACKUP_FILE -C /
sleep2
echo "RESTORE COMPLETE..."
if [[ -e "/proc" ]]; then
echo "$CREATE_DIRS allready exists! "
else
mkdir $CREATE_DIRS
echo "$CREATE_DIRS are created! "
fi
exit
elif [ "$opt" = "DESTINATION" ]; then
echo "CURRENT DESTINATION: $DEST_DIR/backup.`date +%d/%m/%y_%k:%M`.tgz "
echo "TO CHANGE ENTER THE NEW DESTINATION..."
echo "TO LEAVE IT AS IS JUST PRESS ENTER..."
read NEW_DESTIN
#IF GREATER THEN 0 ASSIGN NEW DESTINATION
if [ ${#NEW_DESTIN} -gt 0 ]; then
DESTIN = "$NEW_DESTIN"
fi
clear
echo $BANNER1
echo $BANNER2
echo $BANNER3
echo $LIST
else
clear
echo "BAD INPUT!"
echo "ENTER 1 , 2, 3 or 4.."
echo $LIST
fi
done
Except where you missed the ending quote where you set ROOT_EXCLUDE (line #4), it looks okay to me. I take it the missing quote is a transcription error or your program wouldn't really work at all.
I've tried out the program and it seems to work.
A debugging trick is to put set -xv to turn on debugging in your script and set +xv to turn it off. The -x means to print out the line before executing, and the -v means to print out the line once the shell interpolates the line.
I'm sure that you'll immediately see the issue once you have set -xv in your program.
As part of this, you can set PS4 to the line prompt to print when the debugging information is printed. I like setting PS4 like this:
export PS4="[\$LINENO]> "
This way, the line prompt prints out the line it's executing which is nice.
In your case, I would put set -xv right before you set OPTIONS and then at the very end of the program. This way, you can see the if comparisons and maybe spot your issue.
export PS4="[\$LINENO]> "
set -xv
OPTIONS="BACKUP RESTORE DESTINATION EXIT"
LIST="1)BACKUP 2)RESTORE 3)DESTINATION 4)EXIT"
select opt in $OPTIONS; do
if [ "$opt" = "EXIT" ]; then
echo "GOODBYE!"
set +xv
By the way, it's better to use double square brackets like [[ ... ]] for testing rather than the single square brackets like [ ... ]. This has to do with the way the shell interpolates the values in the test.
The [ ... ] is an alias to the built in test command. The shell interpolates the line as is and the entire line is executed.
The [[ ... ]] are a compound statement where the shell will interpolate variables, but not the entire line. The line is kept as whole:
foo="one thing"
bar="another thing"
This will work:
if [ "$foo" = "$bar" ]
then
echo "Foo and bar are the same"
fi
This won't:
if [ $foo = $bar ]
then
echo "Foo and bar are the same"
fi
The shell interpolates the line as is:
if [ one thing = another thing ]
And this is the same as:
if test one thing = another thing
The test command looks at the first item to see if it's a standard test, or assumes three items and the second item is a comparison. In this case, neither is true.
However, this will work:
if [[ $foo = $bar ]] # Quotes aren't needed
then
echo "Foo and bar are the same"
fi
With the [[ ... ]] being a compound command, the $foo and $bar are replaced with their values, but their positions are kept. Thus, the = is recognized as a comparison operator.
Using [[ ... ]] instead of [ ... ] has solved a lot of hard to find shell scripting bugs I have.

Dynamic dialog --menu box in bash

I'm searching for good explanation about making dynamic dialog --menu box in bash. I'm trying to load a list of users from a file that have structure like this:
------ user ------
/rw412 0.2 /rx511 23.1 /sgo23 9.2
/fs352 1.4 /...
------ another_user ------
/rw412 0.3 / and so on...
of course the user name is between ------
i don't really know how to use loops inside dialog. I'm also trying to avoid creating additional files.
Please help
Here's an example of one way to use dialog. The options array can be built up in a variety of ways (see below).
#!/bin/bash
cmd=(dialog --keep-tite --menu "Select options:" 22 76 16)
options=(1 "Option 1"
2 "Option 2"
3 "Option 3"
4 "Option 4")
choices=$("${cmd[#]}" "${options[#]}" 2>&1 >/dev/tty)
for choice in $choices
do
case $choice in
1)
echo "First Option"
;;
2)
echo "Second Option"
;;
3)
echo "Third Option"
;;
4)
echo "Fourth Option"
;;
esac
done
Here's one way to build the options array:
count=0
while read -r dashes1 username dashes2
do
if [[ $dashes1 == --*-- && $dashes2 == --*-- ]]
then
options+=($((++count)) "$username")
fi
done < inputfile
following the above clues and having my own ideas as well; here is another way:
#!/bin/bash
MENU_OPTIONS=
COUNT=0
for i in `ls`
do
COUNT=$[COUNT+1]
MENU_OPTIONS="${MENU_OPTIONS} ${COUNT} $i off "
done
cmd=(dialog --separate-output --checklist "Select options:" 22 76 16)
options=(${MENU_OPTIONS})
choices=$("${cmd[#]}" "${options[#]}" 2>&1 >/dev/tty)
for choice in $choices
do
" WHATEVER from HERE"
done
Ok
Following Dennis Williamson clues and my own ideas i came to something like this
#!/bin/bash
c=0
while read -r dashes1 username dashes2
do
if [[ $dashes1 == --*-- && $dashes2 == --*-- ]]
then
options=("${options[#]}" "$((++c))" "$username")
fi
done < inputfile
cmd=(dialog --backtitle "title" --menu "Select_user:" 22 38 2) #2 becouse
i know there will be 2 options
command=`echo "${cmd[#]}" "${options[#]}" "2>file"`
$command
Now, there is an error like this:
Error: Expected 2 arguments, found only 1.
Why is that??
This is a repost of my answer from a very similar question. I arrived at this answer with the help of a cohort but couldn't find it in the wild; I think adding it here might help others.
The ${options[#]} array required the following (rather strange) structure in order to work. This was tested on bash in Ubuntu 18.04:
#Dynamic dialogs require an array that has a staggered structure
#array[1]=1
#array[2]=First_Menu_Option
#array[3]=2
#array[4]=Second_Menu_Option
Here is bash code that reads in a directory listing from an argument and creates a dynamic menu from it:
#! /bin/bash
#usage: Dynamic_Menu.bash /home/user/target_directory
declare -a array
i=1 #Index counter for adding to array
j=1 #Option menu value generator
while read line
do
array[ $i ]=$j
(( j++ ))
array[ ($i + 1) ]=$line
(( i=($i+2) ))
done < <(find $1 -type f) #consume file path provided as argument
#Define parameters for menu
TERMINAL=$(tty) #Gather current terminal session for appropriate redirection
HEIGHT=20
WIDTH=76
CHOICE_HEIGHT=16
BACKTITLE="Back_Title"
TITLE="Dynamic Dialog"
MENU="Choose a file:"
#Build the menu with variables & dynamic content
CHOICE=$(dialog --clear \
--backtitle "$BACKTITLE" \
--title "$TITLE" \
--menu "$MENU" \
$HEIGHT $WIDTH $CHOICE_HEIGHT \
"${array[#]}" \
2>&1 >$TERMINAL)

Resources