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?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 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 %sn" "$p_out"
printf "Argument arg_1 is %sn" "$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 exits the program.
Method 2
This is not a parser for positioned arguments, is for key=value aa=bb arguments;
for ARGUMENT in "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c2e682">[email protected]</a>"
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:
Method 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.
Method 4
The probably closest syntax to that is:
p_out='/some/path' arg_1='5' my_script
Method 5
I just came up with this script
while [ $# -gt 0 ]; do
if [[ $1 == *"--"* ]]; then
v="${1/--/}"
declare $v="$2"
fi
shift
done
pass it like my_script --p_out /some/path --arg_1 5 and then in the script you can use $arg_1 and $p_out.
Method 6
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.
Method 7
This answer was initially an edit of @cdmo’s answer (thanks to @Milkncookiez’s comment also!), that got rejected as expected.
When using the variables, one can make sure that they have a default value set by using "${p_out:-"default value"}" for example.
From 3.5.3 Shell Parameter Expansion of the GNU Bash manual:
${parameter:-word}
If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
#!/usr/bin/env bash
while [ $# -gt 0 ]; do
case "$1" in
-p|-p_out|--p_out)
p_out="$2"
;;
-a|-arg_1|--arg_1)
arg_1="$2"
;;
*)
printf "***************************n"
printf "* Error: Invalid argument.*n"
printf "***************************n"
exit 1
esac
shift
shift
done
echo "Without default values:"
echo "p_out: ${p_out}"
echo "arg_1: ${arg_1}"
echo
echo "With default values:"
echo "p_out: ${p_out:-"27"}"
echo "arg_1: ${arg_1:-"smarties cereal"}"
The below invocations are all equivalent:
$ ./my-script.sh -a "lofa" -p "miez" $ ./my-script.sh -arg_1 "lofa" --p_out "miez" $ ./my-script.sh --arg_1 "lofa" -p "miez"
Method 8
I found the solution from cdmo the best, because it is not only limited to a single letter. With a slight adjustment it will consume either whitespace separated arguments like --url www.example.com and those with an assignment like, for example the synonym parameter -u=www.example.com. Even flag arguments can be parsed:
while [ $# -gt 0 ]; do
case "$1" in
--url*|-u*)
if [[ "$1" != *=* ]]; then shift; fi # Value is next arg if no `=`
URL="${1#*=}"
;;
--file*|-f*)
if [[ "$1" != *=* ]]; then shift; fi
FILE="${1#*=}"
;;
--help|-h)
printf "Meaningful help message" # Flag argument
exit 0
;;
*)
>&2 printf "Error: Invalid argumentn"
exit 1
;;
esac
shift
done
Method 9
I made a combination of some approaches recommended here. In my case, I need a script that could handle arguments in this format: --arg1=test
So I used the following:
args=()
for argument in "[email protected]"
do
key=$(echo $argument | cut -f1 -d=)
value=$(echo $argument | cut -f2 -d=)
if [[ $key == *"--"* ]]; then
v="${key/--/}"
declare $v="${value}"
fi
done
args+=( '--local_dir' ${local_dir})
args+=( '--format' ${format})
python3 ./test.py "${args[@]}"
You can see that I added the arguments together to call a python script with those arguments:
test_script.sh --local_dir=test --format=other
Thanks to Shahzad Malik and JRichardsz, for the guide to join the two approaches.
Method 10
I have just made this one.
It doesn’t require “=” and supports non-valued parameters.
Mind the need of additional shift commands for valued parameters.
#!/bin/bash
if [ $# -eq 0 ]; then
printf "Utilizacao:n"
printf "$0 [[--imagem_inicial|-i] <tag_inicial>] [[--imagem_final|-f] <tag_final>] [--verbose|-v]n"
exit 1
fi
while [ $# -gt 0 ]; do
case "$1" in
--imagem_inicial|-i)
export tag_imagem_inicial="${2}"
shift
;;
--imagem_final|-f)
export tag_imagem_final="${2}"
shift
;;
--verbose|-v)
export verbose_option=1
;;
*)
printf "ERRO: Parametros invalidosn"
printf "Execute sem parametros para a sintaxe.n"
exit 1
esac
shift
done
echo tag_imagem_inicial=${tag_imagem_inicial}
echo tag_imagem_final=${tag_imagem_final}
Method 11
My solution is based on that of JRichardsz, but fixing a few issues and adding more transparency about what’s actually going on − I’m not a fan of “copy this random code that you don’t understand, and run it on your personal computer. I swear it’s safe” 😛
for argument in "[email protected]"
do
key=$(echo $argument | cut --fields 1 --delimiter='=')
value=$(echo $argument | cut --fields 2 --delimiter='=')
case "$key" in
"wallet") wallet="$value" ;;
"network") network="$value" ;;
*)
esac
done
echo "We'll be using wallet $wallet and network $network"
Invoking this script can be done as:
myScript.sh wallet=myWallet.json network=https://granadanet.smartpy.io
Method 12
If a function or an application has more than zero arguments, it always has a last argument.
If you want to read option flag and value pairs, as in:
$ ./t.sh -o output -i input -l last
And you want to accept a variable number of option/value pairs,
And do not want a huge “if .. then .. else .. fi” tree,
Then after checking for an argument count of non-zero and even,
Write a while loop with these four eval statements as the body, followed by a case statement using the two values determined in each pass through the loop.
The tricky part of the scripting is demonstrated here:
#!/bin/sh # For each pair - this chunk is hard coded for the last pair. eval TMP="'$'$#" eval "PICK=$TMP" eval TMP="'$'$(($#-1))" eval "OPT=$TMP" # process as required - usually a case statement on $OPT echo "$OPT n $PICK" # Then decrement the indices (as in third eval statement) :<< EoF_test $ ./t.sh -o output -i input -l last -l last $ ./t.sh -o output -l last -l last $ ./t.sh -l last -l last EoF_test
Method 13
<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="422f2b36312d31023027262a23367076">[email protected]</a>$ my_script "a=1;b=mitsos;c=karamitsos" #!/bin/sh eval "$1"
you’ve just injected command line parameters inside script scope !!
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0