Passing named arguments to shell scripts
Solution 1
If you don't mind being limited to single-letter argument names i.e. my_script -p '/some/path' -a5
, then in bash you could use the built-in getopts
, e.g.
#!/bin/bash
while getopts ":a:p:" opt; do
case $opt in
a) arg_1="$OPTARG"
;;
p) p_out="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
exit 1
;;
esac
case $OPTARG in
-*) echo "Option $opt needs a valid argument"
exit 1
;;
esac
done
printf "Argument p_out is %s\n" "$p_out"
printf "Argument arg_1 is %s\n" "$arg_1"
Then you can do
$ ./my_script -p '/some/path' -a5
Argument p_out is /some/path
Argument arg_1 is 5
There is a helpful Small getopts tutorial or you can type help getopts
at the shell prompt.
Edit: The second case
statement in while
loop triggers if the -p
option has no arguments and is followed by another option, e.g. my_script -p -a5
, and exit
s the program.
Solution 2
This is not a parser for positioned arguments, is for key=value aa=bb
arguments;
for ARGUMENT in "$@"
do
KEY=$(echo $ARGUMENT | cut -f1 -d=)
KEY_LENGTH=${#KEY}
VALUE="${ARGUMENT:$KEY_LENGTH+1}"
export "$KEY"="$VALUE"
done
# use here your expected variables
echo "STEPS = $STEPS"
echo "REPOSITORY_NAME = $REPOSITORY_NAME"
echo "EXTRA_VALUES = $EXTRA_VALUES"
Usage
bash args_shell_parser.sh STEPS="ABC" REPOSITORY_NAME="stackexchange" \
EXTRA_VALUES="KEY1=VALUE1 KEY2=VALUE2"
Console result:
STEPS = ABC
REPOSITORY_NAME = stackexchange
EXTRA_VALUES = KEY1=VALUE1 KEY2=VALUE2
Features:
- It does not matter what order the arguments are in
- Explicit declaration of all variables are not required
- values could have spaces.
- It handles complex cases, when the argument value contains "=" sign
Put this snippet at the start of your script.
Prior version of this script:
Solution 3
I stole this from drupal.org, but you could do something like this:
while [ $# -gt 0 ]; do
case "$1" in
--p_out=*)
p_out="${1#*=}"
;;
--arg_1=*)
arg_1="${1#*=}"
;;
*)
printf "***************************\n"
printf "* Error: Invalid argument.*\n"
printf "***************************\n"
exit 1
esac
shift
done
The only caveat is that you have to use the syntax my_script --p_out=/some/path --arg_1=5
.
Solution 4
The probably closest syntax to that is:
p_out='/some/path' arg_1='5' my_script
Solution 5
With zsh
, you'd use zparseopts
:
#! /bin/zsh -
zmodload zsh/zutil
zparseopts -A ARGUMENTS -p_out: -arg_1:
p_out=$ARGUMENTS[--p_out]
arg1=$ARGUMENTS[--arg_1]
printf 'Argument p_out is "%s"\n' "$p_out"
printf 'Argument arg_1 is "%s"\n' "$arg_1"
But you'd call the script with myscript --p_out foo
.
Note that zparseopts
doesn't support abbreviating long options or the --p_out=foo
syntax like GNU getopt(3)
does.
Related videos on Youtube
Amelio Vazquez-Reina
I'm passionate about people, technology and research. Some of my favorite quotes: "Far better an approximate answer to the right question than an exact answer to the wrong question" -- J. Tukey, 1962. "Your title makes you a manager, your people make you a leader" -- Donna Dubinsky, quoted in "Trillion Dollar Coach", 2019.
Updated on September 18, 2022Comments
-
Amelio Vazquez-Reina over 1 year
Is there any easy way to pass (receive) named parameters to a shell script?
For example,
my_script -p_out '/some/path' -arg_1 '5'
And inside
my_script.sh
receive them as:# I believe this notation does not work, but is there anything close to it? p_out=$ARGUMENTS['p_out'] arg1=$ARGUMENTS['arg_1'] printf "The Argument p_out is %s" "$p_out" printf "The Argument arg_1 is %s" "$arg1"
Is this possible in Bash or Zsh?
-
Admin over 6 yearsRelevant: stackoverflow.com/questions/5499472/…
-
-
chepner about 10 yearsRelated to this, if the
-k
option is set in the calling shell, thenmy_script p_out='/some/path' arg_1='5'
has the same effect. (All arguments in the form of an assignment are added to the environment, not just those assignments preceding the command.) -
Kevin about 7 yearsI know this is a bit old, but why only 1 letter for the arguments?
-
Milkncookiez almost 7 yearsThe caveat is not necessary. :) You can have the conditions as follows:
-c|--condition)
-
Timo over 6 yearsDo you know why the zparseopts uses just one dash for the arguments whereas in the
[]
it is 2 dashes? Does not make sense! -
Stéphane Chazelas over 6 years@Timo, see
info zsh zparseopts
for details -
Milkncookiez over 6 yearsI implemented this (but with
i
andd
). When I run it withmy_script -i asd -d asd
I get an empty string for thed
argument. When I run itmy_script -d asd -i asd
I get empty string for both arguments. -
Derek almost 6 years@Milkncookiez -- I had a similar problem -- I didn't include a ':' after the last argument (a 'w' in my case). Once I added the ':' it started working as expected
-
k6t back over 5 yearsI like this solution in KSH88 I had to
v=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"
(Remove one backtick each side) -
Astronaut over 4 yearsThis is a neat solution, but it seems to take a long time to parse, like 5 to 10 seconds on my PC, is this normal behavior?
-
JRichardsz over 4 yearsmmm in my case it is instantaneous.
-
Amelio Vazquez-Reina over 4 years@KaushikGhose Just fixed it. I guess better late than never. I wish SO notified you when an answer other than the one you accepted got more accepted votes.
-
toraritte almost 4 yearsDocs to truncate string in variable (and to continue the trend in this thread, this should be the accepted answer).
-
Jerry Green over 3 yearsThis is neat but assumes you have
=
delimiter between the key and the value. I.e. you can't pass a value with a space like so:-p 3000
or--port 3000
-
StayFoolish about 3 years@chepner, Hi, I like your solution, but I don't know where to put the '-k' option as you mentioned, can you show me the code? thanks
-
chepner about 3 years@StayFoolish One way would be to run
set -k
, then runmy_script p_out='/some/path' arg_1='5'
. -
PaulNUK about 3 yearsUnfortunately this breaks if the value has an equals in it like this MYVAR="PROPERTIES_LIST":"\"apim=true&whichvalue=345\""
-
JRichardsz about 3 yearsseveral systems will have problems with a variable like that. An equivalent could be
PROPERTIES_LIST=apim:true&whichvalue:345
-
davolfman almost 3 yearsArguably there's a second answer using getopt (no s) for long args. I'm not experienced to write it though.
-
JRichardsz over 2 yearsThanks for your suggestion #BohdanShulha It had an error. I fixed: gist.github.com/jrichardsz/…