Created
May 26, 2020 19:35
-
-
Save walkoncross/db914686fe4b4aaecdb566b62c3bc470 to your computer and use it in GitHub Desktop.
How do I parse command line arguments in Bash, from: https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Method #1: Using bash without getopt[s] | |
| Two common ways to pass key-value-pair arguments are: | |
| Bash Space-Separated (e.g., --option argument) (without getopt[s]) | |
| Usage demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts | |
| cat >/tmp/demo-space-separated.sh <<'EOF' | |
| #!/bin/bash | |
| POSITIONAL=() | |
| while [[ $# -gt 0 ]] | |
| do | |
| key="$1" | |
| case $key in | |
| -e|--extension) | |
| EXTENSION="$2" | |
| shift # past argument | |
| shift # past value | |
| ;; | |
| -s|--searchpath) | |
| SEARCHPATH="$2" | |
| shift # past argument | |
| shift # past value | |
| ;; | |
| -l|--lib) | |
| LIBPATH="$2" | |
| shift # past argument | |
| shift # past value | |
| ;; | |
| --default) | |
| DEFAULT=YES | |
| shift # past argument | |
| ;; | |
| *) # unknown option | |
| POSITIONAL+=("$1") # save it in an array for later | |
| shift # past argument | |
| ;; | |
| esac | |
| done | |
| set -- "${POSITIONAL[@]}" # restore positional parameters | |
| echo "FILE EXTENSION = ${EXTENSION}" | |
| echo "SEARCH PATH = ${SEARCHPATH}" | |
| echo "LIBRARY PATH = ${LIBPATH}" | |
| echo "DEFAULT = ${DEFAULT}" | |
| echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l) | |
| if [[ -n $1 ]]; then | |
| echo "Last line of file specified as non-opt/last argument:" | |
| tail -1 "$1" | |
| fi | |
| EOF | |
| chmod +x /tmp/demo-space-separated.sh | |
| /tmp/demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts | |
| output from copy-pasting the block above: | |
| FILE EXTENSION = conf | |
| SEARCH PATH = /etc | |
| LIBRARY PATH = /usr/lib | |
| DEFAULT = | |
| Number files in SEARCH PATH with EXTENSION: 14 | |
| Last line of file specified as non-opt/last argument: | |
| #93.184.216.34 example.com | |
| Bash Equals-Separated (e.g., --option=argument) (without getopt[s]) | |
| Usage demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts | |
| cat >/tmp/demo-equals-separated.sh <<'EOF' | |
| #!/bin/bash | |
| for i in "$@" | |
| do | |
| case $i in | |
| -e=*|--extension=*) | |
| EXTENSION="${i#*=}" | |
| shift # past argument=value | |
| ;; | |
| -s=*|--searchpath=*) | |
| SEARCHPATH="${i#*=}" | |
| shift # past argument=value | |
| ;; | |
| -l=*|--lib=*) | |
| LIBPATH="${i#*=}" | |
| shift # past argument=value | |
| ;; | |
| --default) | |
| DEFAULT=YES | |
| shift # past argument with no value | |
| ;; | |
| *) | |
| # unknown option | |
| ;; | |
| esac | |
| done | |
| echo "FILE EXTENSION = ${EXTENSION}" | |
| echo "SEARCH PATH = ${SEARCHPATH}" | |
| echo "LIBRARY PATH = ${LIBPATH}" | |
| echo "DEFAULT = ${DEFAULT}" | |
| echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l) | |
| if [[ -n $1 ]]; then | |
| echo "Last line of file specified as non-opt/last argument:" | |
| tail -1 $1 | |
| fi | |
| EOF | |
| chmod +x /tmp/demo-equals-separated.sh | |
| /tmp/demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts | |
| output from copy-pasting the block above: | |
| FILE EXTENSION = conf | |
| SEARCH PATH = /etc | |
| LIBRARY PATH = /usr/lib | |
| DEFAULT = | |
| Number files in SEARCH PATH with EXTENSION: 14 | |
| Last line of file specified as non-opt/last argument: | |
| #93.184.216.34 example.com | |
| To better understand ${i#*=} search for "Substring Removal" in this guide. It is functionally equivalent to `sed 's/[^=]*=//' <<< "$i"` which calls a needless subprocess or `echo "$i" | sed 's/[^=]*=//'` which calls two needless subprocesses. | |
| Method #2: Using bash with getopt[s] | |
| from: http://mywiki.wooledge.org/BashFAQ/035#getopts | |
| getopt(1) limitations (older, relatively-recent getopt versions): | |
| can't handle arguments that are empty strings | |
| can't handle arguments with embedded whitespace | |
| More recent getopt versions don't have these limitations. | |
| Additionally, the POSIX shell (and others) offer getopts which doesn't have these limitations. I've included a simplistic getopts example. | |
| Usage demo-getopts.sh -vf /etc/hosts foo bar | |
| cat >/tmp/demo-getopts.sh <<'EOF' | |
| #!/bin/sh | |
| # A POSIX variable | |
| OPTIND=1 # Reset in case getopts has been used previously in the shell. | |
| # Initialize our own variables: | |
| output_file="" | |
| verbose=0 | |
| while getopts "h?vf:" opt; do | |
| case "$opt" in | |
| h|\?) | |
| show_help | |
| exit 0 | |
| ;; | |
| v) verbose=1 | |
| ;; | |
| f) output_file=$OPTARG | |
| ;; | |
| esac | |
| done | |
| shift $((OPTIND-1)) | |
| [ "${1:-}" = "--" ] && shift | |
| echo "verbose=$verbose, output_file='$output_file', Leftovers: $@" | |
| EOF | |
| chmod +x /tmp/demo-getopts.sh | |
| /tmp/demo-getopts.sh -vf /etc/hosts foo bar | |
| output from copy-pasting the block above: | |
| verbose=1, output_file='/etc/hosts', Leftovers: foo bar | |
| The advantages of getopts are: | |
| It's more portable, and will work in other shells like dash. | |
| It can handle multiple single options like -vf filename in the typical Unix way, automatically. | |
| The disadvantage of getopts is that it can only handle short options (-h, not --help) without additional code. | |
| There is a getopts tutorial which explains what all of the syntax and variables mean. In bash, there is also help getopts, which might be informative. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment