-
-
Save jehiah/855086 to your computer and use it in GitHub Desktop.
#!/bin/sh | |
# | |
# a simple way to parse shell script arguments | |
# | |
# please edit and use to your hearts content | |
# | |
ENVIRONMENT="dev" | |
DB_PATH="/data/db" | |
function usage() | |
{ | |
echo "if this was a real script you would see something useful here" | |
echo "" | |
echo "./simple_args_parsing.sh" | |
echo "\t-h --help" | |
echo "\t--environment=$ENVIRONMENT" | |
echo "\t--db-path=$DB_PATH" | |
echo "" | |
} | |
while [ "$1" != "" ]; do | |
PARAM=`echo $1 | awk -F= '{print $1}'` | |
VALUE=`echo $1 | awk -F= '{print $2}'` | |
case $PARAM in | |
-h | --help) | |
usage | |
exit | |
;; | |
--environment) | |
ENVIRONMENT=$VALUE | |
;; | |
--db-path) | |
DB_PATH=$VALUE | |
;; | |
*) | |
echo "ERROR: unknown parameter \"$PARAM\"" | |
usage | |
exit 1 | |
;; | |
esac | |
shift | |
done | |
echo "ENVIRONMENT is $ENVIRONMENT"; | |
echo "DB_PATH is $DB_PATH"; |
Better than writing the parsing code is having one generated and that's the reason why I have started the argbash project here on Github. Keeping the usage
function up-to-date and having support for positional arguments are some of the obvious benefits.
There is even an online bash 3.0+ compatible parsing code generator at https://argbash.io/generate
I've started the run-sections project if you need more than just parsing arguments. With run-sections you can chose what part of you code do you want to run and much more. You can simply mark sections inside your script and run just that sections. If you are interested on this idea, please have a look on https://github.com/stancufm/run-sections . It's very easy to work with this function.
./simple_args_parsing.sh
./simple_args_parsing.sh: 13: ./simple_args_parsing.sh: Syntax error: "(" unexpected
Soo... not great.
Hotfix this
function usage()
{
With :
usage() {
Nice gist conception!
Just a small detail, add -e to echo commands in the help menu, so it will format special tab characters ...
echo "\t-h --help"
shouldn't it be echo -e? Otherwise it prints \t in my case.
Or better use printf?
@geoff-codes thanks for the snippet! @hypersoft's solution was hella less readable
Thank you a lot
@gperreymond is right, without function keyword is better than with it.
@hypersoft 👎 You've complicated a really simple task.
Thanks, works fine. Use echo -e "\t-h --help"
as @bestjejust suggested for printing out tab.
I have difficulties for parsing "-e" with that, I think it interferes with the 'echo $1
line 25.
This works fine with or without an equal symbol.
while [ "$1" != "" ]; do
PARAM=`printf "%s\n" $1 | awk -F= '{print $1}'`
VALUE=`printf "%s\n" $1 | sed 's/^[^=]*=//g'`
if [[ $VALUE == $PARAM ]]; then
shift
VALUE=$1
fi
...
done
@perepechaev An even better way would be
awk -F= '{OFS="=";$1=""; printf substr($0,2)}'
In case that $VALUE is not provided returns an empty string
This works fine with or without an equal symbol.
while [ "$1" != "" ]; do PARAM=`printf "%s\n" $1 | awk -F= '{print $1}'` VALUE=`printf "%s\n" $1 | sed 's/^[^=]*=//g'` if [[ $VALUE == $PARAM ]]; then shift VALUE=$1 fi ... done
If you change if [[ $VALUE == $PARAM ]]
" to if [ "$VALUE" = "$PARAM" ]
it will be more portable.
The code in the OP was written to be run as /bin/sh, so the [[ ]] and == parts could be a problem since they're not POSIX.
Also, I think it's better to change PARAM=printf "%s\n" $1 | awk -F= '{print $1}'
to PARAM="$(printf "%s\n" $1 | awk -F= '{print $1}')"
and VALUE=printf "%s\n" $1 | sed 's/^[^=]*=//g'
to VALUE="$(printf "%s\n" $1 | sed 's/^[^=]*=//g')"
I wrote this - https://github.com/unfor19/bargs - exactly for this need, so you don't need to write down while case esac shift
thingy, a very simple and flexible implementation
I wrote another optional parser and generator that POSIX-compliant and works with all POSIX shells (e.g. dash, bash 2.0+, ksh88, zsh 3.1+, busybox ash). It is a pure shell function and aims to be compatible with POSIX and GNU option syntax. Its code is as fast and small as possible, and the CC0 license allows you to embed it freely in your scripts. https://github.com/ko1nksm/getoptions
2020-11-19: Add support for abbreviated long options and subcommands.
I used the work of @hypersoft for my own script and made some bug fixes and improvements to meet my needs. Giving back the results, which I have simplified for the sake of clarity.
#!/bin/bash
DIRNAME=$(dirname -- $0)
BASENAME=$(basename -- $0)
usage()
{
echo "USAGE: $BASENAME [OPTIONS]"
echo
echo "OPTIONS"
echo " -r, --require-approval Ask user for security approval when deploying CDK stacks"
echo
echo " Features deployed will be the sum \(union\) of the following options, if none"
echo " is specified then all features will be deployed by default. \(Specify each"
echo " option separately, as a group like -yld will not be understood\)"
echo " -a, --all Deploy all features"
echo " -y, --analytics Deploy the analytics features"
echo
echo " -v, --verbose [level] Output additional info. (can use -v=2 form also)"
echo " Possible levels:"
echo " 0 \(default\) essential info"
echo " 1 more info"
echo " 2 even more info"
echo
echo "POSITIONAL PARAMETERS"
echo " none"
exit
}
# default values for flags. always use 1 for true and 0 for false. use strings for qualifiers.
REQUIREAPPROVAL="--require-approval never"
DEFAULT=1
ALL=0
ANALYTICS=0
VERBOSELEVEL="0"
while [[ $# > 0 ]]
do
echo num $#
[[ $1 =~ ^-h$|^--help$ ]] && {
usage
};
# -- marks end of options and start of positional parameters
[[ $1 == -- ]] && { shift; break; };
# options without parameters
[[ $1 =~ ^-r$|^--require-approval$ ]] && { REQUIREAPPROVAL=; shift 1; continue; };
[[ $1 =~ ^-a$|^--all$ ]] && { ALL=1; DEFAULT=0; shift 1; continue; };
[[ $1 =~ ^-y$|^--analytics$ ]] && { ANALYTICS=1; DEFAULT=0; shift 1; continue; };
# verbose option with optional value. e.g. -v, -v 0, -v 1, --verbose, --verbose 1
[[ $1 =~ ^-v$|^--verbose$ ]] && {
shift 1
[[ $# == 0 ]] && { continue; };
VERBOSELEVEL="$1"
};
# verbose option with = value. e.g. -v=0, -v=1, -v=2, --verbose=1 etc.
[[ $1 =~ ^-v=|^--verbose= ]] && {
VERBOSELEVEL="${1#*=}"
shift 1
continue;
};
break;
done
# validate string parameters
[[ ! $VERBOSELEVEL =~ ^[0-2]$ ]] && {
echo illegal --verbose parameter $VERBOSELEVEL
usage
};
# if verbose 1 or 2
[[ ! $VERBOSELEVEL =~ ^[1-2]$ ]] && {
echo DEFAULT == $DEFAULT
echo ALL == $ALL
echo ANALYTICS == $ANALYTICS
};
# the default is to deploy all stacks
ALL=$(( $ALL || $DEFAULT ))
# Logic for which stacks are deployed based on the features needed
CFNMAIN=$(( $ALL ))
CFNANALYTICS=$(( $ALL || $ANALYTICS ))
# if verbose 2 only
[[ ! $VERBOSELEVEL =~ ^2$ ]] && {
echo CFNMAIN = $CFNMAIN
echo CFNANALYTICS = $CFNANALYTICS
};
[[ $CFNMAIN ]] && {
echo cdk synth cfnMain
echo cdk deploy $REQUIREAPPROVAL cfnMain
};
[[ $CFNANALYTICS ]] && {
echo cdk synth cfnAnalytics
echo cdk deploy $REQUIREAPPROVAL cfnAnalytics
};
echo ":) Deployment complete"
#!/bin/sh
DIRNAME=$(dirname -- $0)
BASENAME=$(basename -- $0)usage()
{
echo "USAGE: $BASENAME [OPTIONS]"
echo
echo "OPTIONS"
echo " -r, --require-approval Ask user for security approval when deploying CDK stacks"
echo
echo " Features deployed will be the sum (union) of the following options, if none"
echo " is specified then all features will be deployed by default. (Specify each"
echo " option separately, as a group like -yld will not be understood)"
echo " -a, --all Deploy all features"
echo " -y, --analytics Deploy the analytics features"
echo
echo " -v, --verbose [level] Output additional info. (can use -v=2 form also)"
echo " Possible levels:"
echo " 0 (default) essential info"
echo " 1 more info"
echo " 2 even more info"
echo
echo "POSITIONAL PARAMETERS"
echo " none"
exit
}default values for flags. always use 1 for true and 0 for false. use strings for qualifiers.
REQUIREAPPROVAL="--require-approval never"
DEFAULT=1
ALL=0
ANALYTICS=0
VERBOSELEVEL="0"while [[ $# > 0 ]]
do
echo num $#
[[$1 =~ ^-h$ |^--help$ ]] && {
usage
};
# -- marks end of options and start of positional parameters
[[ $1 == -- ]] && { shift; break; };# options without parameters [[ $1 =~ ^-r$|^--require-approval$ ]] && { REQUIREAPPROVAL=; shift 1; continue; }; [[ $1 =~ ^-a$|^--all$ ]] && { ALL=1; DEFAULT=0; shift 1; continue; }; [[ $1 =~ ^-y$|^--analytics$ ]] && { ANALYTICS=1; DEFAULT=0; shift 1; continue; }; # verbose option with optional value. e.g. -v, -v 0, -v 1, --verbose, --verbose 1 [[ $1 =~ ^-v$|^--verbose$ ]] && { shift 1 [[ $# == 0 ]] && { continue; }; VERBOSELEVEL="$1" }; # verbose option with = value. e.g. -v=0, -v=1, -v=2, --verbose=1 etc. [[ $1 =~ ^-v=|^--verbose= ]] && { VERBOSELEVEL="${1#*=}" shift 1 continue; }; break;
done
validate string parameters
[[ !
$VERBOSELEVEL =~ ^[0-2]$ ]] && {
echo illegal --verbose parameter $VERBOSELEVEL
usage
};if verbose 1 or 2
[[ !
$VERBOSELEVEL =~ ^[1-2]$ ]] && {
echo DEFAULT == $DEFAULT
echo ALL == $ALL
echo ANALYTICS == $ANALYTICS
};the default is to deploy all stacks
ALL=$(( $ALL || $DEFAULT ))
Logic for which stacks are deployed based on the features needed
CFNMAIN=$(( $ALL ))
CFNANALYTICS=$(( $ALL || $ANALYTICS ))if verbose 2 only
[[ !
$VERBOSELEVEL =~ ^2$ ]] && {
echo CFNMAIN = $CFNMAIN
echo CFNANALYTICS = $CFNANALYTICS
};[[ $CFNMAIN ]] && {
echo cdk synth cfnMain
echo cdk deploy $REQUIREAPPROVAL cfnMain
};[[ $CFNANALYTICS ]] && {
echo cdk synth cfnAnalytics
echo cdk deploy $REQUIREAPPROVAL cfnAnalytics
};echo ":) Deployment complete"
It's marked as /bin/sh, but will not work in posix compliant shells (such as dash).
The "[[ ]]" for comparisons, "~=" regex matching, and "==" instead of "=" are all not supported.
If you prefer using those to the posix equivalents, the easiest thing to do would be to just change it to /bin/bash since those are bashisms.
:)
/bin/bash it is, then!
Shouldn't line https://gist.github.com/jehiah/855086#file-simple_args_parsing-sh-L44 be
shift 2
?I am trying it and the parameters are shifted one by one, when (I think) the loop expects them to move two by two.
Anyway, thanks for this, saves a bunch of time 👍