#!/bin/sh # Simple shell script to parse one command line argument at a time. # # Usage: arg args... # # If the flag is present among the given arguments, its value (if any) will be printed. # Otherwise, the command will fail (subject to the special values below). # Positional arguments are those that either don't begin with a '-', or appear # after a lone "--" argument. # # Special Values: # @ Print all positional arguments (i.e. excluding flags). # Print the Nth positional arg, if present, otherwise fail. # ? Pipe output to /dev/null, but still use success status # to indicate if flag is present. # = If flag is not present, use as the value. # Always succeeds. # | If flag1 is not present, check flag2 instead. # # Examples: # arg '-v|--verbose?' "$@" && verbose=yes || verbose=no # if logfile="$(arg --log "$@")"; then ...; fi # path="$(arg '-p|--path=/tmp/default' "$@")" # for f in $(arg @ "$@"); do ...; done # case "$(arg 1 "$@")" in ... esac # arg verboten.txt'?' "$@" && echo "You can't use that file!" && exit 1 # # Note: space-separated flag/value pairs are not supported, e.g. `ls -I \*.txt` # or `ls --ignore \*.txt`. In these cases, please use the following call # syntax instead: `ls -I\*.txt` or `ls -I=\*.txt` and `ls --ignore=\*.txt`. # Supporting space-separated flag/value pairs would make positional argument # functionality impossible because there would be no way to know whether x or y # is the first positional arg in these examples: `arg 1 -f x y`, `arg 1 --foo x y` # (At least, not without telling `arg` whether or not each flag expects a value) # # Note 2: If you expect arguments to contain "\x1E", you may want to run `arg` # with a custom record separator in the environment variable `RS`. Or, if you want # to customize the output of `arg @ ...` to use a custom output record separator, # you can use the environment variable `ORS`. # [ $# -eq 0 ] && echo "arg: a single-flag argument parser. Usage: arg args..." && exit target="$1"; shift RS=${RS-$'\x1E'} ORS=${ORS-$'\n'} case "$target" in @) printf "%s$RS" "$@" | awk -v RS="$RS" -v ORS="$ORS" 'raw || !/^-/; /^--$/ {raw=1}' ;; --) ! printf "%s$RS" "$@" | awk -v RS="$RS" "/^--$/ {if (++count>1) exit 1}" ;; -[^-]\?) ! printf "%s$RS" "$@" | awk -v RS="$RS" "/^-[^-]*${target#-}/ {exit 1}" ;; *\?) $0 "${target%\?}" "$@" >/dev/null ;; *=*) default="${target#*=}"; $0 "${target%%=*}" "$@" || echo "$default" ;; *\|*) $0 "${target%%\|*}" "$@" || $0 "${target#*\|}" "$@" ;; [1-9]*) n="$target"; [ $# -ge "$n" ] && ORS="$RS" $0 @ "$@" | awk -v RS="$RS" "NR==$n" ;; -[^-]) ! printf "%s$RS" "$@" | awk -v RS="$RS" " /^--$/ {exit 0} /^-[^-]*${target#-}/ {sub(/^-[^-]*${target#-}=?/,\"\"); print; exit 1}" ;; *) ! printf "%s$RS" "$@" | awk -v RS="$RS" " /^--$/ {exit 0} /^$target$/ {exit 1} /^$target=/ {sub(/^$target=/,\"\"); print; exit 1}" ;; esac