Парсинг аругментов в Shell#

Полезные ссылки:

Ручной разбор аргументов#

Ультимативный парсер выглядит следующим образом:

 1optval() {
 2    # GNU-style CLI options parser.
 3    #
 4    # Parse `--opt VAL` and `--opt=VAL` options.
 5    # Usage: optval "$1" "$2"
 6    # Sets variables:
 7    #   opt - option name
 8    #   val - option's value
 9    #   sft - value for shift
10
11    opt="${1%%=*}"; val="${1##*=}"; sft=1
12
13    if [ "$opt" == "$val" ]; then
14        if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
15            val="$2"; sft=2
16        else
17            unset val
18        fi
19    fi
20
21    if [ -z "$val" ]; then
22        echo "Missing argument for option $opt" >&2; exit 1
23    fi
24}
25
26# Print help if no arguments passed
27[[ "$#" == 0 ]] && { echo "Help text"; exit 1; }
28
29# Split combined short options, e.g. '-abc' to '-a' '-b' '-c'
30for args in "$@"; do
31    shift
32    case "$args" in
33        --*)
34            set -- "$@" "$args";;  # save long options
35        -*)
36            args="$(echo "${args:1}" | grep -o . | xargs -I {} echo -n '-{} ')"
37            set -- "$@" $args;;
38        *)
39            set -- "$@" "$args";;  # save positional arguments
40    esac
41done
42
43# Final arguments parser
44while (( "$#" )); do
45    case "$1" in
46        -h|--help) echo "Help text"; exit 0;;
47        -v|--version) echo "v1.0.0"; exit 0;;
48        -c|--config|--config=*) optval "$1" "$2"; config="$arg"; shift "$sft";;
49        --) __args+=("$@"); shift "$#";;  # end of options
50        -*) echo "Error: unknown option: $1" >&2; exit 1;;
51        *)  __args+=("$1"); shift;; # save positional args
52    esac
53done

Код парсера достаточно громоздкий, зато он:

  • обрабатывать короткие опции -a -b и -ab;

  • обрабатывает длинные опции --config FILE и --config=FILE;

  • поддерживает «end of options» --;

  • сохраняет все позиционные аргументы.

POSIX getopts#

У getopts есть неприятный ньюанс, заключающийся в том, что он умеет работать исключительно с короткими опциями. Но на самом деле не проблема научить его работать и с длинными.

 1# Print help if no arguments passed
 2[ "$#" -eq 0 ] && { echo "Help text"; exit 1; }
 3
 4# Transform long options to short ones
 5for arg in "$@"; do
 6    shift
 7    case "$arg" in
 8        --help) set -- "$@" "-h";;
 9        --version) set -- "$@" "-v";;
10        *) set -- "$@" "$arg";;
11    esac
12done
13
14# Parse short options
15while getopts ":hvS" opt; do
16    case "$opt" in
17        h) echo "Help text"; exit 0;;
18        v) echo "v1.0.0"; exit 0;;
19        *) echo "Unknown option $opt" >&2; exit 1;;
20    esac
21done
22
23# Parse positional arguments from $@
24shift $((OPTIND - 1))

При использовании getopts внутри функций надо позаботиться о том, чтобы переменная OPTIND была объявлена локальной, иначе парсер будет вести себя непредсказуемо.

local OPTIND

Это не сработает в строгой POSIX оболочке, т.к. в ней нет команды local.