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.
Related
I want to create a script where I have pre-defined option values.
opt1 opt2 opt3
I will start a script and it will ask me to choose from opt1 - opt3.
Once I pick e.g. opt2, that opt2 will then be passed as a variable.
How can I please achieve it?
You can use the "case" command to check if the user value is on a list of expected values:
#!/bin/bash
selected_option=""
echo " *** MENU ***"
echo "1) Opt1"
echo "2) Opt2"
echo "3) Opt3"
echo -n " Please, enter your option: "
read user_answer
case $user_answer in
1|2|3) selected_option="$user_answer"
;;
*) echo "Invalid Option!"
esac
##### Show the result only if the user selected an valid option #####
if [[ ! -z "${selected_option}" ]]; then
echo "Selected Option: [${selected_option}]"
fi
The '|' can be used to separate the valid options, and it will act as an "or" operator.
The '*' section will be executed if the previous condition is not satisfied, which means a "default" behavior, at this case it will display "Invalid Option" message.
Finally, the last if checks if the variable "${selected_option}" is empty, if not it is printed, but you can do whatever you want with that.
#!/bin/sh
echo "choose an option [ 1 opt1 | 2 opt2 | 3 opt3 ]"
read input
if [ $input == 1 ]
then
output="option 1 was chosen"
elif [ $input == 2 ]
then
output="option 2 was chosen"
elif [ $input == 3 ]
then
output="option 3 was chosen"
else
output="invalid input"
fi
echo "$output"
UPDATE:::
In the comments, I've got an answer. Thank you!
:::
I have this sample, I want it to run multiple commands and give an output but It runs only the first command 'terraform init'. How can tell it to run all of the commands with the if statement that checks if file exists or not?
file = ./launchpad.yaml
function run_with_existing_config
{
option=0
until [ "$option" = "2" ]; do
printf "\n ${menu} 1.) ${normal} Run your Cluster \n"
printf "\n ${menu} 2.) ${normal} Return to the Main Menu \n"
read -p 'Enter choice: ' option
echo ""
case $option in
1 ) run_cluster ;;
2 ) main_menu ;;
3 ) break ;;
* ) tput setf 2;printf "\n ${menu} Please enter ${number} 1 ${menu} or ${number} 2 \n"; tput setf 2;
esac
done
}
function run_cluster
{
terraform init
#terraform apply -auto-approve
if [ ! -e "$file" ]; then
continue
else
mv -i ./$file ./$file.$(date +"%Y-%m-%d_%H-%M").yaml
fi
terraform output --raw mke_cluster > ./$file
cat ./$file
#launchpad apply
exit 0
}
I have this script:
#!/bin/bash
menu()
{
while true; do
opt=$(whiptail \
--title "Select an item" \
--menu "" 20 70 10 \
"1 :" "Apple" \
"2 :" "Banana" \
"3 :" "Cherry" \
"4 :" "Pear" \
3>&1 1>&2 2>&3)
rc=$?
echo "rc=$rc opt=$opt"
if [ $rc -eq 255 ]; then # ESC
echo "ESC"
return
elif [ $rc -eq 0 ]; then # Select/Enter
case "$opt" in
1\ *) echo "You like apples"; return ;;
2\ *) echo "You go for bananas"; return ;;
3\ *) echo "I like cherries too"; return ;;
4\ *) echo "Pears are delicious"; return ;;
*) echo "This is an invalid choice"; return ;;
esac
elif [ $rc -eq 1 ]; then # Cancel
echo "Cancel"
return
fi
done
}
menu
When I press ESC button, the output is as expected:
rc=255 opt=
ESC
Now, by making opt a local variable, the behaviour is different:
...
local opt=$(whiptail \
...
Output:
rc=0 opt=
This is an invalid choice
Can someone explain this?
$? is getting the return code of the local command. Try making the local command and the assignment separate statements:
local opt
opt=$(whiptail ...
I found this wonderful tool to check a bash script for possible bugs...
$ shellcheck myscript
Line 6:
local opt=$(whiptail \
^-- SC2155: Declare and assign separately to avoid masking return values.
$
the situation is as follows: i have Global script with Interactive TUI and several functions, and intermediate script, that must use only 1 function from the global script.
Ex:
#!/bin/bash
echo " INSTRUCTIONS:"
read -rsp $'Press any key to continue... \n' -n1 key
function1 {
}
function2 {
}
function3 {
}
read -r -p "Let's go? [yes/no]: " input
if [[ "$input" != "yes" ]]
then
echo "Process aborted." &&
exit
fi
PS3='(hit the number): '
OPT=("1" "2" "3")
select opt in "${OPT[#]}"
do
case $opt in "1")
function1
break
;;
"2")
function2
break
;;
"3")
function3
break
;;
*)
echo invalid option, please retry
;;
esac
done
exit 0
The question is how to provide the arguments to the Intermediate script the way it will silently use 'function3' only, without getting prompted to do anything Global script does? (read prompts and select choice)
Take the option as a command-line argument, and skip the prompt if it's set. To avoid repeating the case code, put it into a function so it can be called from the main-line code and the select loop.
#!/bin/bash
function1() {
}
function2() {
}
function3() {
}
do_func() {
opt=$1
case $opt in
"1")
function1
;;
"2")
function2
;;
"3")
function3
;;
*)
echo invalid option, please retry
;;
esac
}
if [ -n "$1" ]
then
do_func "$1"
exit 0
fi
echo " INSTRUCTIONS:"
read -rsp $'Press any key to continue... \n' -n1 key
read -r -p "Let's go? [yes/no]: " input
if [[ "$input" != "yes" ]]
then
echo "Process aborted." &&
exit
fi
PS3='(hit the number): '
OPT=("1" "2" "3")
select opt in "${OPT[#]}"
do
do_func "$opt"
done
exit 0
You would then use:
scriptname 3
to run function3.
My script
#!/bin/bash
while :
do
echo "[*] 1 - Create a gnome-terminal"
echo "[*] 2 - Display Ifconfig"
echo -n "Pick an Option > "
read Input
if [ "$Input" = "1" ]; then
gnome-terminal
sleep 3
echo "[*] 1 - Create a gnome-terminal"
echo "[*] 2 - Display Ifconfig"
echo -n "Pick an Option > "
read Input
fi
if [ "$Input" = "2" ]; then
clear
ifconfig
echo "[*] 1 - Create a gnome-terminal"
echo "[*] 2 - Display Ifconfig"
echo -n "Pick an Option > "
read Input
fi
done
Doesn't reuse the Input variable, say if you want to run the ifconfig option multiple times in a row. It rechecks the first variable string to see if it matches "1", and when it doesn't it echoes back the list of options. Any ideas on how to make any input variable accessible regardless of when it is used?
The problem is that you're writing out the menu twice on each iteration. Stop doing that:
#!/bin/bash
while :
do
echo "[*] 1 - Create a gnome-terminal"
echo "[*] 2 - Display Ifconfig"
echo -n "Pick an Option > "
read Input
if [ "$Input" = "1" ]; then
gnome-terminal
sleep 3
fi
if [ "$Input" = "2" ]; then
clear
ifconfig
fi
done
Use select instead of rolling your own menu:
choices=(
"Create a gnome-terminal"
"Display Ifconfig"
)
PS3="Pick an Option > "
select action in "${choices[#]}"; do
case $action in
"${choices[0]}") gnome-terminal; sleep 3;;
"${choices[1]}") clear; ifconfig;;
*) echo invalid selection ;;
esac
done
select gives you an infinite loop by default. If you want to break out of it, put a break statement into the case branch.