Passing multiple arguments to bash - linux

I have a script that launches certain nodes based on what arguments you enter
case "$1" in
start)
if [ "$2" == "puppet" ]; then
set_puppet_variables
check_ES_reqs
start
elif [ "$2" == "puppet1" ]; then
set_puppet1_variables
check_ES_reqs
start
elif [ "$2" == "master" ]; then
set_master_variables
check_ES_reqs
start
fi
if [ "$2" == "" ]; then
set_puppet_variables
check_ES_reqs
start
set_master_variables
check_ES_reqs
start
fi
I want to be able to launch multiple specific nodes for example when I type in command service ES start puppet puppet1 it will then launch only those two nodes.
Is there a way to format the $2 in my logic to accept $3, $4 depending on how many nodes I add? as there will be more. Like making the $2 to a $2* to accept the second argument and any others so I can launch multiple specific nodes.
Please help
Thank you

Shift $1 out of the argument list, then loop over the remaining arguments.
case "$1" in
start)
shift
while [ $# -ne 0 ]
do
case "$1" in
puppet)
... ;;
puppet1)
... ;;
master)
... ;;
"")
... ;;
esac
shift
done ;;

shift off your command, work on the rest. This is more or less what I'm expecting you require:
cmd="$1"
shift; # get rid of it.
if [ $# -eq 0 ]
then
# if there are no parameters, use puppet and master
set -- puppet master
fi
case "$cmd" in
start)
for node in "$#"
do
set_variables "$node" # slight rename here, make it easier to reuse
check_ES_reqs
start
done
;;
# ...
esac

Related

why bash script default case statement always executed?

I wrote a bash script that reads script arguments and pass them to parsearg function:
#!/usr/bin/env bash
function main() {
parseargs2 "$#"
}
function parseargs2() {
MAINCOMMAND=$1
shift
while [ $# -gt 0 ]; do
case $1 in
-s|--service) SERVICE_NAME="$2" ;;
-r|--registry) REGISTRY="$2" ;;
-h|--help) HELP=true ;;
*) echo "help" && exit 1;;
esac
shift
done
echo "SERVICE_NAME: $SERVICE_NAME"
echo "REGISTRY: $REGISTRY"
echo "HELP: $HELP"
echo "cmd: $MAINCOMMAND"
}
main "$#"
now when I run my script it always executes help command and then exits, I don't know why it will be ok when I remove *) case
./example.sh build --service api --registry dockerhub
EDIT:
thanks to #chepner comment I found the problem I solved this by adding shift 2 at end of while loop
Doing your own option parsing is a little fraught, but the idea should be to do an extra shift whenever you have an argument:
while [ $# -gt 0 ]; do
case $1 in
-s|--service) SERVICE_NAME="$2" ; shift;;
-r|--registry) REGISTRY="$2" ; shift;;
-h|--help) HELP=true ;;
*) echo "help" && exit 1;;
esac
shift
done
You may or may not want to support a style where the single-letter version of an option has its argument jammed up against it, which works in many stock programs:
while [ $# -gt 0 ]; do
case $1 in
-s|--service) SERVICE_NAME="$2" ; shift;;
-s*) SERVICE_NAME=${1#-s};;
-r|--registry) REGISTRY="$2" ; shift;;
-r*) REGISTRY=${1#-r};;
-h|--help) HELP=true ;;
*) echo "help" && exit 1;;
esac
shift
done
Also, there's no reason to name your variables in all-caps. All caps is usually a sign that a variable is being exported into the environment so some other program you're going to run can see it; otherwise, just use lowercase. Additionally, you don't need quotation marks on the right-hand side of an assignment, and if you're really using bash (rather than some other POSIX type shell), you likely want to use ((...)) for numeric comparisons.
help= # don't assume variables aren't set on entry
maincommand=$1
shift
while (( $# )); do
case "$1" in
-s|--service) service_name=$2 ; shift;;
-s*) service_name=${1#-s};;
-r|--registry) registry=$2 ; shift;;
-r*) registry=${1#-r};;
-h|--help) help=true ;;
*) echo "help" && exit 1;;
esac
shift
done
for var in service_name registry help maincommand; do
printf '%s: "%s"\n' "$var" "${!var}"
done
FWIW, true is just a string to the shell with no special significance; the shell doesn't have Boolean values beyond "command succeeded/failed" (which is just zero/nonzero on the exit code) or Boolean operators other than the ones that operate on commands (e.g. command1 && run this if command1 succeeded). So when flags are stored in shell variables the Booleanness is usually represented either by empty vs. nonempty string (and true is a fine value to use for the nonempty case) or 0 vs. 1 (or 0 vs nonzero) number.

bash script options parser fails

I have a bashscript.sh that I $ chmod +x bashscript.sh and move it to $ mv bashscript.sh ~/.local/bin/ in order for it to be executable like a system command.
I'd like to be able to invoke it with
bashscript [<-w|-working|--working>[=|:]] <y|yes|n|no>
And return usage/help/error (call it whatever we want) if the call isn't respected.
To do so, I wrote this parsing part:
usage(){
echo "you're wrong."
exit 1
}
[[ $# -lt 1 ]] && usage
options=$(getopt -o y,n,h,w: -l yes,no,help,working: -- "$#")
set -- $options
while true; do
case "$1" in
-h|--help|*) usage
shift;;
y|yes)
#do something
shift;;
n|no)
#..
shift;;
-w|-working|--working)
shift;;
--) #this is just syntax
shift
break;;
esac
done
But when I test it doesn't work as intended*, would you know why/have a sample that handles my option possibilites?
*edit : I always trigger the usage display
edit 2 : removed the spaces around the "=" of options as #costaparas3 pointed out, thank you, still stuck to usage() though
Here are the issues I found:
Exit if there are no arguments
Set the options with options=$(... (no spaces)
-h|--help|*) matches everything so you have an infinite loop. You don't need to match on * as getopt will reuturn non-zero if it finds an invalid argument, and the match on -- is what usually terminates the loop.
getopt returns non-zero for invalid arguments so exit 1 then
Use -n|--no to specify short and long options
--working requires an argument but you only shift 1.
-working is not valid (with getopt). Use either -w or --working.
Here is corrected version:
#!/bin/bash
usage() {
echo "you're wrong."
exit $1
}
[ $# -lt 1 ] && usage
options=$(getopt -o y,n,h,w: -l yes,no,help,working: -- "$#")
[ $? -ne 0 ] && usage 1
# default values
yes=
working=
set -- $options
while :
do
case "$1" in
-h|--help)
usage
;;
-y|--yes)
yes=1
shift
;;
-n|--no)
yes=0 # or no=1
shift
;;
-w|--working)
working=$2
shift 2
;;
--)
break
;;
esac
done

Bash: using getops for this specific use

I would need my bash scrip to work either with:
No arguments
./script.sh
Path argument (classic argument, will saved as var to var=$1)
./script.sh /root/home/dir/
With switch -a (with it's own argument)
./script.sh -a picture.jpeg
Or both combined
./script.sh -a picture.jpeg /root/home/dir/
I have something like this:
while getopts ":a:" opt; do
case $opt in
a)
I_ARGUMENT=$OPTARG
echo "A ARGUMENT IS: $OPTARG"
;;
:)
echo "-a requires argument"
;;
esac
done
And then something like this for the path argument:
if [ -z "$1" ]
then
:
else
PATH="$1"
fi
Which obviously doesn't work well (at all) together. Could you help me to combine this two things? Thank you.
How about something like this
pathToHomeSlashDir=~/dir/ #default value will be home/dir/
if [ $# -gt 0 ]
then
while [ $# -gt 0 ]
do
case "$1" in
-a)
if [ $# -gt 1 ]
then
image="$2"
shift
else
echo "Please insert -a argument"
exit
fi
;;
*)
pathToHomeSlashDir="$1"
;;
esac
shift
done
fi
You'd just need to handle the variables then

Setting variable to last arg from command line parameters

I'm trying to write a script in a way that makes it simple to add future command line args. I'm using getopts and that works well. However, I'm running into issues with the case statement.
args=`getopt lLo:t: $*`
if [ $? -ne 0 ]
then
echo "Usage: Default error message"
fi
while [ "$#" -eq 3 ] || [ "$#" -eq 5 ]
do
if [ "$1" != "-o" ] && [ "$1" != "-t" ]
then
echo "\nInvalid argument sequence."
exit 1
fi
case "$1" in
(-o)
shift
VAR1="$1"
shift
;;
(-t)
shift
VAR2="$1"
shift
;;
(*)
LAST_VAR="$1"
;;
(--) shift; break;;
esac
done
If I then echo $LAST_VAR it is null. Any ideas? Is there a better way to do this?
Some findings:
You need spaces around each [ and ] (line 2). See help [.
You don't need ( in case matchings. See help case.
You can use VAR1="$2" and shift 2 instead of two separate shifts. See help shift.
You want to put the -- case before *, and move the break to the * case.
Follow the logic backwards: LAST_VAR will be set if $1 is neither -o nor -t. Before that you exit if $1 is neither -o nor -t. So LAST_VAR can never be set.
I would suggest looking at some getopt examples for some ideas. Shameless plug for some tested code:
# Process parameters
params="$(getopt -o d:e:fshv \
-l diff:,exclude:,force,skip-existing,help,verbose \
--name "$cmdname" -- "$#")"
if [ $? -ne 0 ]
then
usage
fi
eval set -- "$params"
unset params
while true
do
case $1 in
-d|--diff)
diff_exec=(${2-})
shift 2
;;
-e|--exclude)
# Will override $default_excludes
excludes+=("${2-}")
shift 2
;;
-f|--force)
action='R'
shift
;;
-s|--skip-existing)
action='S'
shift
;;
-h|--help)
usage
exit
;;
-v|--verbose)
verbose='--verbose'
shift
;;
--)
shift
if [ -z "${1:-}" ]
then
error "Missing targets." "$help_info" $EX_USAGE
fi
if [ -z "${2:-}" ]
then
error "Missing directory." "$help_info" $EX_USAGE
fi
targets=(${#:1:$(($#-1))})
source_dir="${#:$#}"
break
;;
*)
usage
;;
esac
done

Multiple option arguments using getopts (bash)

I am trying to process command line arguments using getopts in bash. One of the requirements is for the processing of an arbitrary number of option arguments (without the use of quotes).
1st example (only grabs the 1st argument)
madcap:~/projects$ ./getoptz.sh -s a b c
-s was triggered
Argument: a
2nd example (I want it to behave like this but without needing to quote the argument"
madcap:~/projects$ ./getoptz.sh -s "a b c"
-s was triggered
Argument: a b c
Is there a way to do this?
Here's the code I have now:
#!/bin/bash
while getopts ":s:" opt; do
case $opt in
s) echo "-s was triggered" >&2
args="$OPTARG"
echo "Argument: $args"
;;
\?) echo "Invalid option: -$OPTARG" >&2
;;
:) echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
I think what you want is to get a list of values from a single option. For that, you can repeat the option as many times as needed, and add it's argument to an array.
#!/bin/bash
while getopts "m:" opt; do
case $opt in
m) multi+=("$OPTARG");;
#...
esac
done
shift $((OPTIND -1))
echo "The first value of the array 'multi' is '$multi'"
echo "The whole list of values is '${multi[#]}'"
echo "Or:"
for val in "${multi[#]}"; do
echo " - $val"
done
The output would be:
$ /tmp/t
The first value of the array 'multi' is ''
The whole list of values is ''
Or:
$ /tmp/t -m "one arg with spaces"
The first value of the array 'multi' is 'one arg with spaces'
The whole list of values is 'one arg with spaces'
Or:
- one arg with spaces
$ /tmp/t -m one -m "second argument" -m three
The first value of the array 'multi' is 'one'
The whole list of values is 'one second argument three'
Or:
- one
- second argument
- three
This's my way to do this with a user-defined-function: getopts-extra.
function getopts-extra () {
declare i=1
# if the next argument is not an option, then append it to array OPTARG
while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do
OPTARG[i]=${!OPTIND}
let i++ OPTIND++
done
}
# Use it within the context of `getopts`:
while getopts s: opt; do
case $opt in
s) getopts-extra "$#"
args=( "${OPTARG[#]}" )
esac
done
The full example of getoptz.sh:
#!/usr/bin/env bash
function getopts-extra () {
declare i=1
# if the next argument is not an option, then append it to array OPTARG
while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do
OPTARG[i]=${!OPTIND}
let i++ OPTIND++
done
}
function main () {
declare args
declare OPTIND OPTARG opt
while getopts :s: opt; do
case $opt in
s) getopts-extra "$#"
args=( "${OPTARG[#]}" )
;;
\?) echo "Invalid option: -$OPTARG" >&2
;;
:) echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
declare i
for i in "${!args[#]}"; do
echo "args[$i]: ${args[i]}"
done
}
main "$#"
exit
Test:
bash getoptz.sh -s a b c
Output:
args[0]: a
args[1]: b
args[2]: c
This function is a part of the bash lib called xsh-lib/core, available at the syntax xsh /util/getopts/extra.
You can parse the command-line arguments yourself, but the getopts command cannot be configured to recognize multiple arguments to a single option. fedorqui's recommendation is a good alternative.
Here is one way of parsing the option yourself:
while [[ "$*" ]]; do
if [[ $1 = "-s" ]]; then
# -s takes three arguments
args="$2 $3 $4"
echo "-s got $args"
shift 4
fi
done
Instead of using getops I found this better. Create a file tmp.sh and copy the following code..
usage() {
echo "Usage:
./tmp.sh --cloud_provider gcp --source_path abc --destination_path def --refresh_frequency 100 --authenticate true --credential_path a.json
"
}
while [ "$#" -gt 0 ]; do
case $1 in
-a | --authenticate)
shift
[[ $1 == -* ]] && continue
export authenticate=$1
;;
-cp | --credential_path)
shift
[[ $1 == -* ]] && continue
export credential_path=$1
;;
-c | --cloud_provider)
shift
[[ $1 == -* ]] && continue
export cloud_provider=$1
;;
-s | --source_path)
shift
[[ $1 == -* ]] && continue
export source_path=$1
;;
-d | --destination_path)
shift
[[ $1 == -* ]] && continue
export destination_path=$1
;;
-r | --refresh_frequency)
shift
[[ $1 == -* ]] && continue
export refresh_frequency=$1
;;
--)
shift
break
;;
esac
shift
done

Resources