Skip to content

Instantly share code, notes, and snippets.

@jehiah
Created March 4, 2011 16:56
Show Gist options
  • Save jehiah/855086 to your computer and use it in GitHub Desktop.
Save jehiah/855086 to your computer and use it in GitHub Desktop.
a simple way to parse shell script arguments
#!/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";
@Ivancaminal72
Copy link

@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

@cslycord
Copy link

cslycord commented Apr 30, 2020

@Pagliacii

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')"

@unfor19
Copy link

unfor19 commented Jul 23, 2020

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

@ko1nksm
Copy link

ko1nksm commented Nov 8, 2020

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.

@alanmur
Copy link

alanmur commented Nov 8, 2021

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"

@slycordinator
Copy link

#!/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.

:)

@alanmur
Copy link

alanmur commented Nov 9, 2021

/bin/bash it is, then!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment