Как проанализировать аргументы командной строки в Bash?

Скажем, у меня есть script, который вызывается с помощью этой строки:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

или этот:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

Что принято в этом синтаксическом анализе, чтобы в каждом случае (или некоторая комбинация из двух) $v, $f и $d все были установлены на true, а $outFile будет равно до /fizz/someOtherFile?

1594
10 окт. '08 в 19:57
источник поделиться
32 ответов
  • 1
  • 2

Метод №1: Использование bash без getopt [s]

Два общих способа передачи аргументов пары "ключ-значение":

Bash Space -s скомпонован (например, --option argument) (без getopt [s])

Использование ./myscript.sh -e conf -s/etc -l/usr/lib/etc/hosts

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

Bash Equals -s (например, --option=argument) (без getopt [s])

Использование ./myscript.sh -e=conf -s=/etc -l=/usr/lib/etc/hosts

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

Чтобы лучше понять ${i#*=} поиск "Удаление подстроки" в этом руководстве. Он функционально эквивалентен 'sed 's/[^=]*=//' <<< "$i"' который вызывает ненужный подпроцесс или 'echo "$i" | sed 's/[^=]*=//'' 'echo "$i" | sed 's/[^=]*=//'' который вызывает два ненужных подпроцесса.

Метод №2: Использование bash с getopt [s]

от: http://mywiki.wooledge.org/BashFAQ/035#getopts

Ограничения getopt (1) (более старые, относительно недавние версии getopt):

  • не может обрабатывать аргументы, которые являются пустыми строками
  • не может обрабатывать аргументы со встроенными пробелами

Более поздние версии getopt не имеют этих ограничений.

Кроме того, оболочка POSIX (и другие) предлагает getopts который не имеет этих ограничений. Вот упрощенный пример getopts:

#!/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: $@"

# End of file

Преимущества getopts:

  1. Он более портативен и будет работать в других оболочках, таких как dash.
  2. Он может обрабатывать несколько отдельных параметров, таких как -vf filename в типичном способе Unix, автоматически.

Недостатком getopts является то, что он может обрабатывать только короткие варианты (-h, а не --help) без дополнительного кода.

Существует учебник getopts, в котором объясняется, что означает весь синтаксис и переменные. В bash есть также help getopts, которая может быть информативной.

2249
07 янв. '13 в 23:01
источник

Связанные вопросы


Похожие вопросы

Нет ответа упоминает расширенный Getopt. И верхний ответ -v вводит в заблуждение: он либо игнорирует короткие опции в стиле -⁠vfd (запрошенные OP), либо опции после позиционных аргументов (также запрошенные OP); и он игнорирует ошибки синтаксического анализа. Вместо:

  • Используйте улучшенный getopt из util-linux или ранее GNU glibc. 1
  • Он работает с getopt_long() функцией C в GNU glibc.
  • Имеет все полезные отличительные особенности (у других их нет):
    • обрабатывает пробелы, заключая в кавычки символы и даже двоичные в аргументах 2 (не расширенный getopt не может этого сделать)
    • в конце он может обрабатывать опции: script.sh -o outFile file1 file2 -v (getopts не делает)
    • позволяет = -style длинные опции: script.sh --outfile=fileOut --infile fileIn (разрешение обоих script.sh --outfile=fileOut --infile fileIn если сам разбирается)
    • позволяет комбинировать короткие опции, например -vfd (реальная работа, если сам разбирается)
    • позволяет трогать аргументы-опции, например -oOutfile или -vfdoOutfile
  • Уже настолько 3 года, что ни в одной системе GNU его нет (например, в любом Linux).
  • Вы можете проверить его существование с помощью: getopt --test → возвращаемое значение 4.
  • Другие getopt или встроенные в оболочку getopts имеют ограниченное использование.

Следующие звонки

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

все возвращаются

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

со следующей myscript

#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset

# -allow a command to fail with !s side effect on errexit
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo 'Im sorry, 'getopt --test' failed in this environment.'
    exit 1
fi

OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose

# -regarding ! and PIPESTATUS see above
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out "--options")
# -pass arguments only via   -- "$@"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopts output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

1 расширенный getopt доступен на большинстве "bash-систем", включая Cygwin; на OS X попробуйте установить bnu gnu-getopt или sudo port install getopt
2 соглашения POSIX exec() не имеют надежного способа передачи двоичного NULL в аргументах командной строки; эти байты преждевременно заканчивают спор
3 первая версия, выпущенная в 1997 году или ранее (я отслеживал ее только в 1997 году)

444
20 апр. '15 в 20:47
источник

от: digitalpeer.com с небольшими изменениями

Использование myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

Чтобы лучше понять ${i#*=} найдите "Удаление подстроки" в этом руководстве. Функционально он эквивалентен 'sed 's/[^=]*=//' <<< "$i"' который вызывает ненужный подпроцесс или 'echo "$i" | sed 's/[^=]*=//'' 'echo "$i" | sed 's/[^=]*=//'' который вызывает два ненужных подпроцесса.

117
13 нояб. '12 в 13:31
источник

getopt()/getopts() - хороший вариант. Украден из здесь:

Простое использование "getopt" показано в этом мини- script:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

Мы сказали, что любой из -a, -b, -c или -d, но за -c следует аргумент (говорит "c:" ).

Если мы назовем это "g" и попробуем:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

Начнем с двух аргументов и "getopt" разрывает варианты и ставит каждого в свои собственные аргументы. Это также добавлено "-".

103
10 окт. '08 в 20:03
источник

Рискуя добавить другой пример для игнорирования, вот моя схема.

  • обрабатывает -n arg и --name=arg
  • разрешает аргументы в конце
  • показывает правильные ошибки, если что-то написано неправильно.
  • совместимый, не использует bashisms
  • читаемый, не требует сохранения состояния в цикле

Надеюсь, что это полезно кому-то.

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;

    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done
90
16 июля '15 в 2:43
источник

Более лаконичный способ

script.sh

#!/bin/bash

while [[ "$#" -gt 0 ]]; do case $1 in
  -d|--deploy) deploy="$2"; shift;;
  -u|--uglify) uglify=1;;
  *) echo "Unknown parameter passed: $1"; exit 1;;
esac; shift; done

echo "Should deploy? $deploy"
echo "Should uglify? $uglify"

Использование:

./script.sh -d dev -u

# OR:

./script.sh --deploy dev --uglify
89
20 нояб. '15 в 15:28
источник

Я опаздываю на этот вопрос на 4 года, но хочу вернуть. Я использовал более ранние ответы в качестве отправной точки, чтобы убрать мой старый синтаксический анализ adhoc. Затем я переработал следующий код шаблона. Он обрабатывает как длинные, так и короткие параметры, используя аргументы = или пробел, а также несколько коротких параметров, сгруппированных вместе. Наконец, он снова вставляет любые аргументы без параметров обратно в переменные $1, $2.. Надеюсь, это будет полезно.

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done


# Code template for parsing command line parameters using only portable shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS


echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done
39
01 июля '14 в 4:20
источник

Мой ответ в основном основан на ответе Бруно Броноски, но я как бы размял его две чистые реализации bash в один, который я использую довольно часто.

# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
    key="$1"
    case "$key" in
        # This is a flag type option. Will catch either -f or --foo
        -f|--foo)
        FOO=1
        ;;
        # Also a flag type option. Will catch either -b or --bar
        -b|--bar)
        BAR=1
        ;;
        # This is an arg value type option. Will catch -o value or --output-file value
        -o|--output-file)
        shift # past the key and to the value
        OUTPUTFILE="$1"
        ;;
        # This is an arg=value type option. Will catch -o=value or --output-file=value
        -o=*|--output-file=*)
        # No need to shift here since the value is part of the same string
        OUTPUTFILE="${key#*=}"
        ;;
        *)
        # Do whatever you want with extra options
        echo "Unknown option '$key'"
        ;;
    esac
    # Shift after checking all the cases to get the next option
    shift
done

Это позволяет вам иметь как разделенные пробелом опции/значения, так и равные определенные значения.

Итак, вы можете запустить script, используя:

./myscript --foo -b -o /fizz/file.txt

а также:

./myscript -f --bar -o=/fizz/file.txt

и оба должны иметь тот же конечный результат.

ПЛЮСЫ:

  • Позволяет использовать как -arg = значение и -arg значение

  • Работает с любым именем arg, которое вы можете использовать в bash

    • Значение -a или -arg или -arg или -a-r-g или что-то еще
  • Pure bash. Нет необходимости изучать/использовать getopt или getopts

МИНУСЫ:

  • Невозможно объединить args

    • Значение no -abc. Вы должны сделать -a -b -c

Это единственные плюсы/минусы, которые я могу придумать с головы.

26
08 сент. '16 в 21:59
источник

Я нашел, что нужно написать переносимый синтаксический анализ в сценариях, так что разочарование в том, что я написал Argbash - генератор кода FOSS, который может генерировать код анализа аргументов для вашего script плюс он имеет некоторые приятные функции:

https://argbash.io

26
11 июля '16 в 1:40
источник

Я думаю, что это достаточно просто, чтобы использовать:

#!/bin/bash
#

readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'

opts=vfdo:

# Enumerating options
while eval $readopt
do
    echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done

# Enumerating arguments
for arg
do
    echo ARG:$arg
done

Пример вызова:

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile
13
01 марта '12 в 18:15
источник

Развернувшись на отличном ответе от @guneysus, вот настройка, которая позволяет пользователю использовать любой синтаксис, который они предпочитают, например

command -x=myfilename.ext --another_switch 

против

command -x myfilename.ext --another_switch

То есть равные могут быть заменены пробелами.

Эта "нечеткая интерпретация" может быть не по вашему вкусу, но если вы создаете скрипты, которые взаимозаменяемы с другими утилитами (как в случае с моими, которые должны работать с ffmpeg), гибкость полезна.

STD_IN=0

prefix=""
key=""
value=""
for keyValue in "$@"
do
  case "${prefix}${keyValue}" in
    -i=*|--input_filename=*)  key="-i";     value="${keyValue#*=}";; 
    -ss=*|--seek_from=*)      key="-ss";    value="${keyValue#*=}";;
    -t=*|--play_seconds=*)    key="-t";     value="${keyValue#*=}";;
    -|--stdin)                key="-";      value=1;;
    *)                                      value=$keyValue;;
  esac
  case $key in
    -i) MOVIE=$(resolveMovie "${value}");  prefix=""; key="";;
    -ss) SEEK_FROM="${value}";          prefix=""; key="";;
    -t)  PLAY_SECONDS="${value}";           prefix=""; key="";;
    -)   STD_IN=${value};                   prefix=""; key="";; 
    *)   prefix="${keyValue}=";;
  esac
done
13
09 июня '14 в 16:46
источник

getopts отлично работает, если # 1 у вас он установлен, а # 2 вы собираетесь запускать его на той же платформе. OSX и Linux (например) ведут себя по-другому в этом отношении.

Вот решение (не getopts), которое поддерживает равные, не равные и логические флаги. Например, вы можете запустить свой скрипт таким образом:

./script --arg1=value1 --arg2 value2 --shouldClean

# parse the arguments.
COUNTER=0
ARGS=("$@")
while [ $COUNTER -lt $# ]
do
    arg=${ARGS[$COUNTER]}
    let COUNTER=COUNTER+1
    nextArg=${ARGS[$COUNTER]}

    if [[ $skipNext -eq 1 ]]; then
        echo "Skipping"
        skipNext=0
        continue
    fi

    argKey=""
    argVal=""
    if [[ "$arg" =~ ^\- ]]; then
        # if the format is: -key=value
        if [[ "$arg" =~ \= ]]; then
            argVal=$(echo "$arg" | cut -d'=' -f2)
            argKey=$(echo "$arg" | cut -d'=' -f1)
            skipNext=0

        # if the format is: -key value
        elif [[ ! "$nextArg" =~ ^\- ]]; then
            argKey="$arg"
            argVal="$nextArg"
            skipNext=1

        # if the format is: -key (a boolean flag)
        elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
            argKey="$arg"
            argVal=""
            skipNext=0
        fi
    # if the format has not flag, just a value.
    else
        argKey=""
        argVal="$arg"
        skipNext=0
    fi

    case "$argKey" in 
        --source-scmurl)
            SOURCE_URL="$argVal"
        ;;
        --dest-scmurl)
            DEST_URL="$argVal"
        ;;
        --version-num)
            VERSION_NUM="$argVal"
        ;;
        -c|--clean)
            CLEAN_BEFORE_START="1"
        ;;
        -h|--help|-help|--h)
            showUsage
            exit
        ;;
    esac
done
8
13 февр. '15 в 0:50
источник

Я даю вам функцию parse_params которая будет анализировать параметры из командной строки.

  1. Это чисто решение Bash, никаких дополнительных утилит.
  2. Не загрязняет глобальные рамки.
  3. Без усилий возвращает вам простые в использовании переменные, на которых вы могли бы построить дополнительную логику.
  4. Количество тире перед параметрами не имеет значения (--all равно -all равно all=all)

Сценарий ниже является рабочей демонстрацией копирования-вставки. Смотрите функцию show_use чтобы понять, как использовать parse_params.

Ограничения:

  1. Не поддерживает разделенные пробелом параметры (-d 1)
  2. Имена параметров потеряют тире, поэтому --any-param и -anyparam эквивалентны
  3. eval $(parse_params "$@") должен использоваться внутри функции bash (он не будет работать в глобальной области видимости)

#!/bin/bash

# Universal Bash parameter parsing
# Parse equal sign separated params into named local variables
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Puts un-named params as-is into ${ARGV[*]} array
# Additionally puts all named params as-is into ${ARGN[*]} array
# Additionally puts all standalone "option" params as-is into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4.1 (Jul-27-2018)
parse_params ()
{
    local existing_named
    local ARGV=() # un-named params
    local ARGN=() # named params
    local ARGO=() # options (--params)
    echo "local ARGV=(); local ARGN=(); local ARGO=();"
    while [[ "$1" != "" ]]; do
        # Escape asterisk to prevent bash asterisk expansion, and quotes to prevent string breakage
        _escaped=${1/\*/\'\"*\"\'}
        _escaped=${_escaped//\'/\\\'}
        _escaped=${_escaped//\"/\\\"}
        # If equals delimited named parameter
        nonspace="[^[:space:]]"
        if [[ "$1" =~ ^${nonspace}${nonspace}*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # val is everything after key and = (protect from param==value error)
            local _val="${1/$_key=}"
            # remove dashes from key name
            _key=${_key//\-}
            # skip when key is empty
            # search for existing parameter name
            if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
                # if name already exists then it a multi-value named parameter
                # re-declare it as an array if needed
                if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
                    echo "$_key=(\"\$$_key\");"
                fi
                # append new value
                echo "$_key+=('$_val');"
            else
                # single-value named parameter
                echo "local $_key='$_val';"
                existing_named=" $_key"
            fi
        # If standalone named parameter
        elif [[ "$1" =~ ^\-${nonspace}+ ]]; then
            # remove dashes
            local _key=${1//\-}
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # Add to options array
            echo "ARGO+=('$_escaped');"
            echo "local $_key=\"$_key\";"
        # non-named parameter
        else
            # Escape asterisk to prevent bash asterisk expansion
            _escaped=${1/\*/\'\"*\"\'}
            echo "ARGV+=('$_escaped');"
        fi
        shift
    done
}

#--------------------------- DEMO OF THE USAGE -------------------------------

show_use ()
{
    eval $(parse_params "$@")
    # --
    echo "${ARGV[0]}" # print first unnamed param
    echo "${ARGV[1]}" # print second unnamed param
    echo "${ARGN[0]}" # print first named param
    echo "${ARG0[0]}" # print first option param (--force)
    echo "$anyparam"  # print --anyparam value
    echo "$k"         # print k=5 value
    echo "${multivalue[0]}" # print first value of multi-value
    echo "${multivalue[1]}" # print second value of multi-value
    [[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}

show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2
8
01 июля '16 в 23:56
источник

EasyOptions не требует синтаксического анализа:

## Options:
##   --verbose, -v  Verbose mode
##   --output=FILE  Output filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "output file is ${output}"
    echo "${arguments[@]}"
fi
7
04 июля '16 в 19:47
источник

Вот как я делаю в функции, чтобы избежать нарушения работы getopts в то же время где-то выше в стеке:

function waitForWeb () {
   local OPTIND=1 OPTARG OPTION
   local host=localhost port=8080 proto=http
   while getopts "h:p:r:" OPTION; do
      case "$OPTION" in
      h)
         host="$OPTARG"
         ;;
      p)
         port="$OPTARG"
         ;;
      r)
         proto="$OPTARG"
         ;;
      esac
   done
...
}
6
19 июля '13 в 10:50
источник

Я хотел бы предложить свою версию синтаксического анализа параметров, которая позволяет следующее:

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

Также позволяет это (может быть нежелательно):

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

Вы должны решить перед использованием if = для использования по опции или нет. Это значит, что код чист (ish).

while [[ $# > 0 ]]
do
    key="$1"
    while [[ ${key+x} ]]
    do
        case $key in
            -s*|--stage)
                STAGE="$2"
                shift # option has parameter
                ;;
            -w*|--workfolder)
                workfolder="$2"
                shift # option has parameter
                ;;
            -e=*)
                EXAMPLE="${key#*=}"
                break # option has been fully handled
                ;;
            *)
                # unknown option
                echo Unknown option: $key #1>&2
                exit 10 # either this: my preferred way to handle unknown options
                break # or this: do this to signal the option has been handled (if exit isn't used)
                ;;
        esac
        # prepare for next option in this key, if any
        [[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
    done
    shift # option(s) fully processed, proceed to next input argument
done
4
24 июня '15 в 13:54
источник

Обратите внимание, что getopt(1) была короткой живой ошибкой от AT & T.

getopt был создан в 1984 году, но уже похоронен в 1986 году, потому что он не был действительно полезен.

Доказательство того, что getopt очень устарело, состоит в том, что man-страница getopt(1) по-прежнему упоминает "$*" вместо "$@", которая была добавлена ​​в оболочку Bourne в 1986 году вместе с оболочкой getopts(1) встроенный для обработки аргументов с пробелами внутри.

BTW: если вам интересно разобрать длинные параметры в сценариях оболочки, может быть интересно узнать, что реализация getopt(3) из libc (Solaris) и ksh93 добавила единую длинную опционную реализацию, которая поддерживает длинные параметры как псевдонимы для коротких опций. Это приводит к тому, что ksh93 и Bourne Shell реализуют единый интерфейс для длинных опций через getopts.

Пример для длинных параметров, взятых из страницы руководства Bourne Shell:

getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"

показывает, как долго альтернативные псевдонимы могут использоваться как в Bourne Shell, так и в ksh93.

См. справочную страницу недавней оболочки Борна:

http://schillix.sourceforge.net/man/man1/bosh.1.html

и справочную страницу для getopt (3) из OpenSolaris:

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

и, наконец, справочную страницу getopt (1) для проверки устаревшего $*:

http://schillix.sourceforge.net/man/man1/getopt.1.html

4
19 окт. '15 в 16:59
источник

Предположим, мы создаем оболочку script с именем test_args.sh, как следует

#!/bin/sh
until [ $# -eq 0 ]
do
  name=${1:1}; shift;
  if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi  
done
echo "year=$year month=$month day=$day flag=$flag"

После выполнения следующей команды:

sh test_args.sh  -year 2017 -flag  -month 12 -day 22 

Вывод будет:

year=2017 month=12 day=22 flag=true
3
11 окт. '17 в 1:49
источник

Я хочу представить свой проект: https://github.com/flyingangel/argparser

source argparser.sh
parse_args "$@"

Просто как тот. Окружающая среда будет заполнена переменными с тем же именем, что и аргументы

2
16 сент. '18 в 20:45
источник

Я пишу помощник bash, чтобы написать хороший инструмент bash

project home: https://gitlab.mbedsys.org/mbedsys/bashopts

Пример:

#!/bin/bash -ei

# load the library
. bashopts.sh

# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"

# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"

# Parse arguments
bashopts_parse_args "$@"

# Process argument
bashopts_process_args

поможет:

NAME:
    ./example.sh - This is myapp tool description displayed on help message

USAGE:
    [options and commands] [-- [extra args]]

OPTIONS:
    -h,--help                          Display this help
    -n,--non-interactive true          Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
    -f,--first "John"                  First name - [$first_name] (type:string, default:"")
    -l,--last "Smith"                  Last name - [$last_name] (type:string, default:"")
    --display-name "John Smith"        Display name - [$display_name] (type:string, default:"$first_name $last_name")
    --number 0                         Age - [$age] (type:number, default:0)
    --email                            Email adress - [$email_list] (type:string, default:"")

наслаждайтесь:)

2
21 февр. '17 в 0:30
источник

Смешивание позиционных и флаговых аргументов

- param = arg (равно разграничен)

Свободно смешивание флагов между позиционными аргументами:

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

может выполняться с достаточно кратким подходом:

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   param=${!pointer}
   if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      case $param in
         # paramter-flags with arguments
         -e=*|--environment=*) environment="${param#*=}";;
                  --another=*) another="${param#*=}";;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
         || set -- ${@:((pointer + 1)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

- param arg (пробел)

Обычно проще не смешивать стили --flag=value и --flag value.

./script.sh dumbo 127.0.0.1 --environment production -q -d

Это немного рискованно читать, но все еще актуально

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

Источник

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      param=${!pointer}
      ((pointer_plus = pointer + 1))
      slice_len=1

      case $param in
         # paramter-flags with arguments
         -e|--environment) environment=${!pointer_plus}; ((slice_len++));;
                --another) another=${!pointer_plus}; ((slice_len++));;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
         || set -- ${@:((pointer + $slice_len)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2
2
27 апр. '15 в 5:42
источник

Вот мой подход - с помощью regexp.

  • no getopts
  • он обрабатывает блок коротких параметров -qwerty
  • он обрабатывает короткие параметры -q -w -e
  • он обрабатывает длинные параметры --qwerty
  • вы можете передать атрибут короткому или длинному варианту (если вы используете блок коротких опций, атрибут привязан к последней опции)
  • вы можете использовать пробелы или = для предоставления атрибутов, но атрибут соответствует до тех пор, пока не встретит дефис + пробел "разделитель", поэтому в --q=qwe ty qwe ty есть один атрибут
  • он обрабатывает смешение всего выше, поэтому -o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute действительно

script:

#!/usr/bin/env sh

help_menu() {
  echo "Usage:

  ${0##*/} [-h][-l FILENAME][-d]

Options:

  -h, --help
    display this help and exit

  -l, --logfile=FILENAME
    filename

  -d, --debug
    enable debug
  "
}

parse_options() {
  case $opt in
    h|help)
      help_menu
      exit
     ;;
    l|logfile)
      logfile=${attr}
      ;;
    d|debug)
      debug=true
      ;;
    *)
      echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
      exit 1
  esac
}
options=$@

until [ "$options" = "" ]; do
  if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
    if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
      opt=${BASH_REMATCH[3]}
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
      pile=${BASH_REMATCH[4]}
      while (( ${#pile} > 1 )); do
        opt=${pile:0:1}
        attr=""
        pile=${pile/${pile:0:1}/}
        parse_options
      done
      opt=$pile
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    else # leftovers that don't match
      opt=${BASH_REMATCH[10]}
      options=""
    fi
    parse_options
  fi
done
2
15 марта '17 в 16:24
источник

Решение, которое сохраняет необработанные аргументы. Включены демонстрации.

Вот мое решение. Он ОЧЕНЬ гибкий и в отличие от других, не должен требовать внешних пакетов и обрабатывать оставшиеся аргументы чисто.

Использование: ./myscript -flag flagvariable -otherflag flagvar2

Все, что вам нужно сделать, это отредактировать строку validflags. Он добавляет дефис и ищет все аргументы. Затем он определяет следующий аргумент как имя флага, например.

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

Основной код (короткая версия, подробный с примерами ниже, также версия с ошибкой):

#!/usr/bin/env bash
#shebang.io
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
    for flag in $validflags
    do
        sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers

Вербальная версия со встроенными эхо-демонстрациями:

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
echo "all args
$@"
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
#   argval=$(echo $@ | cut -d ' ' -f$count)
    for flag in $validflags
    do
            sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done

#Cleanup then restore the leftovers
echo "pre final clear args:
$@"
shift $#
echo "post final clear args:
$@"
set -- $leftovers
echo "all post set args:
$@"
echo arg1: $1 arg2: $2

echo leftovers: $leftovers
echo rate $rate time $time number $number

Заключительный, это ошибка, если недопустимый -аргумент передан.

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in $@
do
    argval=$1
    match=0
        if [ "${argval:0:1}" == "-" ]
    then
        for flag in $validflags
        do
                sflag="-"$flag
            if [ "$argval" == "$sflag" ]
            then
                declare $flag=$2
                match=1
            fi
        done
        if [ "$match" == "0" ]
        then
            echo "Bad argument: $argval"
            exit 1
        fi
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers

Плюсы: Что он делает, он отлично справляется. Он сохраняет неиспользуемые аргументы, которые многие другие решения здесь нет. Он также позволяет вызывать переменные без указания вручную в script. Он также позволяет предустановить переменные, если не указан соответствующий аргумент. (См. Подробный пример).

Минусы: не удается разобрать одну сложную строку arg, например. -xcvf будет обрабатываться как один аргумент. Вы могли бы легко написать дополнительный код в мой, который добавляет эту функциональность.

2
29 авг. '16 в 6:44
источник

Вот мое улучшенное решение Bruno Bronosky, используя переменные массивы.

он позволяет вам смешивать положение параметров и давать вам массив параметров, сохраняющий порядок без параметров

#!/bin/bash

echo $@

PARAMS=()
SOFT=0
SKIP=()
for i in "$@"
do
case $i in
    -n=*|--skip=*)
    SKIP+=("${i#*=}")
    ;;
    -s|--soft)
    SOFT=1
    ;;
    *)
        # unknown option
        PARAMS+=("$i")
    ;;
esac
done
echo "SKIP            = ${SKIP[@]}"
echo "SOFT            = $SOFT"
    echo "Parameters:"
    echo ${PARAMS[@]}

Выведет, например:

$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP            = .c .obj
SOFT            = 1
Parameters:
parameter somefile
1
06 окт. '15 в 11:53
источник

Верхний ответ на этот вопрос казался немного затруднительным, когда я его попробовал - вот мое решение, которое я нашел более надежным:

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi
1
08 авг. '16 в 15:42
источник

Расширяя ответ @bruno-bronosky, я добавил "препроцессор" для обработки некоторых распространенных форматов:

  • Расширяет --longopt=val в --longopt val
  • Расширяет -xyz в -x -y -z
  • Поддерживает -- чтобы указать конец флагов
  • Показывает ошибку для неожиданных опций
  • Компактный и легко читаемый переключатель параметров
#!/bin/bash

# Report usage
usage() {
  echo "Usage:"
  echo "$(basename $0) [options] [--] [file1, ...]"

  # Optionally exit with a status code
  if [ -n "$1" ]; then
    exit "$1"
  fi
}

invalid() {
  echo "ERROR: Unrecognized argument: $1" >&2
  usage 1
}

# Pre-process options to:
# - expand -xyz into -x -y -z
# - expand --longopt=arg into --longopt arg
ARGV=()
END_OF_OPT=
while [[ $# -gt 0 ]]; do
  arg="$1"; shift
  case "${END_OF_OPT}${arg}" in
    --) ARGV+=("$arg"); END_OF_OPT=1 ;;
    --*=*)ARGV+=("${arg%%=*}" "${arg#*=}") ;;
    --*) ARGV+=("$arg"); END_OF_OPT=1 ;;
    -*) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;;
    *) ARGV+=("$arg") ;;
  esac
done

# Apply pre-processed options
set -- "${ARGV[@]}"

# Parse options
END_OF_OPT=
POSITIONAL=()
while [[ $# -gt 0 ]]; do
  case "${END_OF_OPT}${1}" in
    -h|--help)      usage 0 ;;
    -p|--password)  shift; PASSWORD="$1" ;;
    -u|--username)  shift; USERNAME="$1" ;;
    -n|--name)      shift; names+=("$1") ;;
    -q|--quiet)     QUIET=1 ;;
    -C|--copy)      COPY=1 ;;
    -N|--notify)    NOTIFY=1 ;;
    --stdin)        READ_STDIN=1 ;;
    --)             END_OF_OPT=1 ;;
    -*)             invalid "$1" ;;
    *)              POSITIONAL+=("$1") ;;
  esac
  shift
done

# Restore positional parameters
set -- "${POSITIONAL[@]}"
1
05 марта '19 в 20:12
источник

Другое решение без getopt [s], POSIX, старый стиль Unix

Похоже на решение Bruno Bronosky опубликовано, это здесь без использования getopt(s).

Основная отличительная особенность моего решения заключается в том, что он позволяет объединять параметры, как tar -xzf foo.tar.gz, равно tar -x -z -f foo.tar.gz. И так же, как в tar, ps и т.д. Ведущий дефис является необязательным для блока коротких опций (но это можно легко изменить). Также поддерживаются длинные параметры (но когда блок начинается с одного, тогда требуются два ведущих дефиса).

Код с примерами параметров

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

Пример использования примера приведен ниже.

Позиция опций с аргументами

В чем его ценность, параметры с аргументами не являются последними (должны быть только длинные опции). Таким образом, пока, например, в tar (по крайней мере, в некоторых реализациях) параметры f должны быть последними, потому что имя файла следует (tar xzf bar.tar.gz работает, но tar xfz bar.tar.gz) это не так (см. более поздние примеры).

Несколько опций с аргументами

В качестве еще одного бонуса параметры параметра потребляются в порядке параметров по параметрам с требуемыми параметрами. Просто посмотрите на вывод моего script здесь с командной строкой abc X Y Z (или -abc X Y Z):

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

Длинные опции также объединены

Кроме того, вы также можете иметь длинные опции в блоке параметров, учитывая, что они встречаются последними в блоке. Таким образом, следующие командные строки эквивалентны (включая порядок, в котором обрабатываются параметры и его аргументы):

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

Все это приводит к:

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

Не в этом решении

Дополнительные аргументы

Параметры с необязательными аргументами должны быть возможны с небольшим количеством работы, например. глядя вперед, есть ли блок без дефиса; пользователь должен будет поместить дефис перед каждым блоком после блока с параметром, имеющим необязательный параметр. Может быть, это слишком сложно для общения с пользователем, поэтому лучше всего в этом случае просто взять ведущий дефис.

Все становится еще сложнее с несколькими возможными параметрами. Я бы посоветовал не делать параметры, пытаясь быть умными, определяя, может ли для него аргумент или нет (например, с опцией просто принимает число как необязательный аргумент), потому что это может сломаться в будущем.

Я лично предпочитаю дополнительные опции вместо необязательных аргументов.

Опционные аргументы, введенные с знаком равенства

Как и с необязательными аргументами, я не поклонник этого (BTW, есть ли тема для обсуждения плюсов и минусов разных стилей параметров?), но если вы этого хотите, вы, вероятно, могли бы реализовать его сами, как это сделано в http://mywiki.wooledge.org/BashFAQ/035#Manual_loop с оператором case --long-with-arg=?*, а затем снятие знака равенства (это BTW сайт, в котором говорится, что выполнение конкатенации параметров возможно с помощью некоторые усилия, но "оставили [это] упражнение для читателя", что заставило меня взять их по их слову, но я начал с нуля).

Другие примечания

POSIX-совместимый, работает даже на старых настройках Busybox, с которыми мне пришлось иметь дело (например, cut, head и getopts).

1
18 окт. '15 в 0:17
источник

В этом примере показано, как использовать getopt и eval а также HEREDOC и shift для обработки коротких и длинных параметров с и без требуемого значения, которое следует. Кроме того, инструкция switch/case является краткой и легкой для понимания.

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, dont change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <<-EOM
   num=$num_str
   time=$time_str
   verbose=$verbose
   dryrun=$dryrun
EOM
fi

# The rest of your script below

Наиболее значимые строки приведенного выше сценария:

OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Коротко, по сути, читабельно и обрабатывает практически все (ИМХО).

Надеюсь, что это помогает кому-то.

1
07 сент. '16 в 21:25
источник

Это также может быть полезно знать, вы можете установить значение, и если кто-то предоставляет ввод, переопределите значение по умолчанию с этим значением.

myscript.sh -f./serverlist.txt или просто. /myscript.sh(и он принимает значения по умолчанию)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift

    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done


    echo "SERVER LIST   = ${SERVER_FILE_LIST}"
1
14 июня '14 в 21:01
источник

Используйте "аргументы" модуля из bash-modules

Пример:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "$@" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"
1
09 июля '13 в 19:51
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос