Named Arguments in a Bash Script
Tags: bash
Published: 2022-08-31 04:23:08

Let's face it, using positional arguments like `$1` and `$2` for your arguments is not very descriptive and not very flexible. There is a better way to supply arguments: with this simple trick you'll get `--named arguments --in your-script`, which is waaaay better 🤓.

## [Parsing script arguments](#parsing-script-arguments)

The following script parses all arguments and turns them into variables:

```sh
while [ $# -gt 0 ]; do
    if [[ $1 == "--"* ]]; then
        v="${1/--/}"
        declare "$v"="$2"
        shift
    fi
    shift
done

echo "\$this: '$this' \$a: '$a'"
```

When we execute the script with: `./test.sh --this is --a test`, it returns:

```
$this: 'is' $a: 'test'
```

_Note: this method is not very strict, it might skip parameters that are not adhering to the double dash format. If you want to support --single-parameters, check [Support for --help?](#support-for-help)_

_Note 2: this method does not work with `set -u`_ 🤷

## [Argument validation](#argument-validation)

First, let's define a `usage` function to show how our script works:

```sh
programname=$0
function usage {
    echo ""
    echo "Deploys an ECR image to Atlas using GitOps and ArgoCD."
    echo ""
    echo "usage: $programname --service_name string --tag string --atlas string --env string "
    echo ""
    echo "  --service_name string   name of the service"
    echo "                          (example: blaze-search-term-redirect-service)"
    echo "  --tag string            tag of the image to deploy"
    echo "                          (example: 804-a325d6a)"
    echo "  --namespace string      namespace of the cluster"
    echo "                          (example: eos)"
    echo "  --env string            env to which to deploy the tag"
    echo "                          (example: dev)"
    echo ""
}
```

Next, let's define a `die` function:

```sh
function die {
    printf "Script failed: %s\n\n" "$1"
    exit 1
}
```

Now, let's check if the parameters are supplied. If one of the parameters is missing, display usage and terminate the script. We use the [\-z conditional expression](https://www.gnu.org/software/bash/manual/html%5Fnode/Bash-Conditional-Expressions.html) to check if the variable is empty:

```sh
if [[ -z $service_name ]]; then
    usage
    die "Missing parameter --service_name"
elif [[ -z $tag ]]; then
    usage
    die "Missing parameter --tag"
elif [[ -z $namespace ]]; then
    usage
    die "Missing parameter --namespace"
elif [[ -z $env ]]; then
    usage
    die "Missing parameter --env"
fi
```

## [Support for --help?](#support-for-help)

The easiest way of adding `--help` to your script, is by adding it to the loop that turns the arguments into variables:

```sh
while [ $# -gt 0 ]; do
    if [[ $1 == "--help" ]]; then
        usage
        exit 0
    elif [[ $1 == "--"* ]]; then
        v="${1/--/}"
        declare "$v"="$2"
        shift
    fi
    shift
done
```

Any singular parameter can be added to this loop.

## [How about defaults?](#how-about-defaults)

Here you have 2 strategies:

1. Declare the variables at the top of your script and initialize them with defaults.
2. Use [parameter expansion](https://www.gnu.org/software/bash/manual/html%5Fnode/Shell-Parameter-Expansion.html): `my_parameter=${my_parameter:-default value}`

### [Parameter expansion](#parameter-expansion)

If you use parameter expansion, you need to know that the empty string `""` will also be replaced. Observe the following script:

```sh
my_parameter="${my_parameter:-default value}"
printf "%s\n\n" "$my_parameter"
```

It will give the following output:

```sh
$ ./test.sh
default value

$ ./test.sh --my_parameter "my value"
my value

$ ./test.sh --my_parameter ""
default value

```

So, settings the defaults at the top of the script has my preference.

## [Write better scripts: use ShellCheck](#write-better-scripts-use-shellcheck)

The article [_Please stop writing shell scripts_](https://pythonspeed.com/articles/shell-scripts/) makes some excellent points on why writing a bash script is hard for programmers: **bash behavior is most likely not like the programming language you're familiar with**. The [ShellCheck extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck) will help you catch some of the pitfalls. With every "offence", it links to a wiki article for education:

[![Show the explanation of error code SC2086.](https://keestalkstech.com/wp-content/uploads/2022/03/bash-SC2086-952x1024.png)](https://keestalkstech.com/wp-content/uploads/2022/03/bash-SC2086.png)

Show the explanation of [SC2086](https://github.com/koalaman/shellcheck/wiki/SC2181).

## [Conclusion](#conclusion)

Adding named arguments to a bash script is pretty easy. There are some caveats, as you might "override" arguments in your script from the outside. If you declare the arguments at the beginning of your script and the reset of the parameters after the `while` section, you mitigate that risk.

## [Changelog](#changelog)

2022-04-04: made the parameter validation script a bit shorter by using `elif`.