Run multiple commands when function called Bash - bash

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
}

Related

Bash subcommands with arguments

I have a script in bash as such:
#!/usr/bin/env bash
set -e
if [[ "$#" == 0 ]]; then
printhelp
exit 1
fi
# process options
while [[ "$1" != "" ]]; do
case "$1" in
-n | --name)
shift
_NAME="$1"
;;
-i | --id)
shift
_ID="$1"
;;
-h | --help)
printhelp
exit 1
;;
*)
printhelp
exit 1
;;
esac
shift
done
This works fine, but I want to add some "actions" that will take the above params. Eg. usage will be:
./run.sh create --name foo --id 1234
./run.sh delete --id 1234
I am not able to figure out the right syntax, and I am unable to phrase this requirement into appropriate words to be able to search.
For sub-command you can handle it this way:
function main(){
if (( ${#} == 0 )); then
main_help 0;
fi
case ${1} in
help | version | encrypt | decrypt )
$1 "${#:2}";
;;
* )
echo "unknown command: $1";
main_help 1;
exit 1;
;;
esac
}
main "$#";
Then wrap each sub-command is a function. And inside each function you will have isolated options and parsing it separately.
For example:
function decrypt(){
if [[ ${#} == 0 ]]; then
decrypt_help;
fi
local __filename='';
local __salt='';
local __anchor=false;
local error_message='';
while [ ${#} -gt 0 ]; do
error_message="Error: a value is needed for '$1'";
case $1 in
-f | --file )
__filename=${2:?$error_message}
shift 2;
;;
-s | --salt )
__salt=${2:?$error_message}
shift 2;
;;
-a | --anchor )
__anchor=${2:?$error_message}
shift 2;
;;
* )
echo "unknown option $1";
break;
;;
esac
done
echo filename: ${__filename:-empty};
echo salt: ${__salt:-empty};
echo anchor: $__anchor;
exit 0;
}
Here is a full version bash-CLI-template I have used in my projects
demo ;)
Sounds like you want something like:
create() {
# actiony stuff here
}
ACTION=$1 ; shift
# put all your argument parsing here
$ACTION # call
However, since different actions probably have different arguments, I'd probably do it differently...
create() {
# argument parsing for create
# then do your create stuff
}
ACTION=$1 ; shift
$ACTION "$#"
This will pass all your arguments to your subfunction, which can then parse its own arguments.

local variable changes whiptail behaviour

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.
$

BASH: how to silently invoke the external script passing the variables to 'case'

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.

using getopts did not get the input value

I am running the below script, but it looks like the $filename or $srvname did not get the input value.
say for eg: ./test.sh -n abcd.net gives the output echo 'Filename or node name must be defined.'
it means that, the $srvname did not get the value "abcd.net", please advise am i doing anything wrong. ?
set -x
usage () {
echo "usage: $0 -n <nodename>"
echo "usage: $0 -f <filename>"
echo "usage: $0 -h <help>"
}
while getopts ":nfh:" opt; do
case "$opt" in
n) srvname="$OPTARG" ;;
f) filename="$OPTARG" ;;
h) # help
usage
exit 0
;;
:) echo "Error: -$OPTARG requires an argument"
usage
exit 1
;;
?) echo "Error: unknown option -$OPTARG"
usage
exit 1
;;
esac
done
function dosomecheck {
echo "do some checks"
}
if [ "$filename" != "" ] ; then
# read file
for x in `cat $filename` ; do
dosomecheck $x
done
fi
if [ "$srvname" != "" ] ; then
# read file
for x in $srvname ; do
dosomecheck $x
done
fi
Thanks in advance
Try doing:
while getopts ":n:f:h" opt;
because -n and -f takes argument while -h doesn't.

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