Пример использования getopts в bash


345

Я хочу вызвать myscriptфайл следующим образом:

$ ./myscript -s 45 -p any_string

или

$ ./myscript -h  #should display help
$ ./myscript     #should display help

Мои требования:

  • getopt здесь, чтобы получить входные аргументы
  • проверить, что -sсуществует, если не вернуть ошибку
  • проверьте, что значение после -s45 или 90
  • проверьте, что -pсуществует и есть ли строка ввода после
  • если пользователь вводит ./myscript -hили просто ./myscriptотображает справку

Я пробовал пока этот код:

#!/bin/bash
while getopts "h:s:" arg; do
  case $arg in
    h)
      echo "usage" 
      ;;
    s)
      strength=$OPTARG
      echo $strength
      ;;
  esac
done

Но с этим кодом я получаю ошибки. Как это сделать с Bash и getopt?


2
Опции должны быть необязательными. Если вам требуется значение , указанное -s, сделать его позиционное аргумент: ./myscript 45 anystring.
chepner

@chepner$./myscript -s 45 -p any_string
MOHAMED

Хорошо, если -pна самом деле есть опция (то есть ваша программа может продолжаться, если ее нет). В этом случае ./myscript 45 -p any_string. (Я думаю, что он getoptможет обрабатывать смешанные опции и позиционные аргументы, тогда как bashвстроенная команда getoptsтребует, чтобы все позиционные аргументы были помещены после опций.)
chepner

Ответы:


513
#!/bin/bash

usage() { echo "Usage: $0 [-s <45|90>] [-p <string>]" 1>&2; exit 1; }

while getopts ":s:p:" o; do
    case "${o}" in
        s)
            s=${OPTARG}
            ((s == 45 || s == 90)) || usage
            ;;
        p)
            p=${OPTARG}
            ;;
        *)
            usage
            ;;
    esac
done
shift $((OPTIND-1))

if [ -z "${s}" ] || [ -z "${p}" ]; then
    usage
fi

echo "s = ${s}"
echo "p = ${p}"

Пример работы:

$ ./myscript.sh
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -h
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s "" -p ""
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 10 -p foo
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 45 -p foo
s = 45
p = foo

$ ./myscript.sh -s 90 -p bar
s = 90
p = bar

19
В вызове getopts почему ведущий двоеточие? Когда после h стоит двоеточие?
с40

7
Должен ли usage()действительно вернуть 1?
Питикос

6
@Pithikos Хороший вопрос. Здравый смысл подсказывает мне, что при вызове через -hнего он должен возвращаться 0, при нажатии на несуществующий флаг он должен возвращаться >0(для простоты я не делал различий между этими случаями, и никто не заставляет вас печатать текст использования в последнем случае) , Я видел программы, которые всегда возвращаются != 0, хотя и включаются -h/--help. Может быть, мне следует обновить фрагмент на тот случай, если люди используют его как шаблон (надеюсь, нет)?
Адриан Фрювирт,

1
@ A.Danischewski Это ( getopts') дизайн, нет такой вещи как "необязательные аргументы" с getopts. Синтаксический анализатор просто не может знать, является ли следующий токен аргументом текущей опции или самой опцией, поскольку это -pможет быть предполагаемое значение. Вы можете обойти это, если абсолютно уверены, что параметр option не может выглядеть как другой допустимый параметр, да, но можно сказать, что есть причина, по которой необязательные аргументы не определены в POSIX.
Адриан Фрювирт

4
@ user1011471 Вы правы! Фигурные скобки, так сказать, просто помогают bashлексеру в определении переменных. Во многих случаях они не нужны, и тот факт, что я всегда ими пользуюсь, является лишь вопросом стиля персонального кодирования. На мой взгляд, легче (и красивее) просто всегда использовать их, а не вспоминать правила синтаксического анализа в отношении неоднозначности. Почти то же самое, почему пишут if (foo) { bar; }вместо языка if (foo) bar;C-стиля (эстетика и / или избегание глупых ошибок).
Адриан Фрювирт,

109

Проблема с оригинальным кодом заключается в том, что:

  • h:ожидает параметр, где он не должен, поэтому измените его на просто h(без двоеточия)
  • чтобы ожидать -p any_string, вам нужно добавить p:в список аргументов

В основном :после опции означает, что она требует аргумента.


Основной синтаксис getopts: (см . man bash:):

getopts OPTSTRING VARNAME [ARGS...]

где:

  • OPTSTRING строка со списком ожидаемых аргументов,

    • h- проверить вариант -h без параметров; выдает ошибку по неподдерживаемым параметрам;
    • h:- проверить опцию -h с параметром; выдает ошибки по неподдерживаемым параметрам;
    • abc- проверка опций -a, -b, -c; выдает ошибки по неподдерживаемым параметрам;
    • :abc- проверка опций -a, -b, -c; заставляет замолчать ошибки на неподдерживаемых опциях;

      Примечания: Другими словами, двоеточие перед опциями позволяет вам обрабатывать ошибки в вашем коде. Переменная будет содержаться ?в случае неподдерживаемой опции, :в случае пропущенного значения.

  • OPTARG - устанавливается текущее значение аргумента,

  • OPTERR - указывает, должны ли Bash отображать сообщения об ошибках.

Таким образом, код может быть:

#!/usr/bin/env bash
usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; }
[ $# -eq 0 ] && usage
while getopts ":hs:p:" arg; do
  case $arg in
    p) # Specify p value.
      echo "p is ${OPTARG}"
      ;;
    s) # Specify strength, either 45 or 90.
      strength=${OPTARG}
      [ $strength -eq 45 -o $strength -eq 90 ] \
        && echo "Strength is $strength." \
        || echo "Strength needs to be either 45 or 90, $strength found instead."
      ;;
    h | *) # Display help.
      usage
      exit 0
      ;;
  esac
done

Пример использования:

$ ./foo.sh 
./foo.sh usage:
    p) # Specify p value.
    s) # Specify strength, either 45 or 90.
    h | *) # Display help.
$ ./foo.sh -s 123 -p any_string
Strength needs to be either 45 or 90, 123 found instead.
p is any_string
$ ./foo.sh -s 90 -p any_string
Strength is 90.
p is any_string

Смотрите: Учебник по маленьким getopts на Bash Hackers Wiki


2
Измените функцию использования для этого: usage() { echo "$0 usage:" && grep "[[:space:]].)\ #" $0 | sed 's/#//' | sed -r 's/([a-z])\)/-\1/'; exit 0; }. Он учитывает только один символ пробела перед параметром буквы, удаляет знак # из комментария и добавляет «-» перед параметром буквы, делая его более понятным для команды.
poagester

2
@kenorb: двоеточие перед опциями не игнорирует неподдерживаемые опции, но заглушает ошибки от bash и позволяет вам обрабатывать их в своем коде. Переменная будет содержать '?' в случае неподдерживаемой опции и «:» в случае пропущенного значения.
Гинек -Пичи- Выходил

1
Спасибо за подробные документы, не смог получить :права, пока не увидел эти заметки. Нам нужно добавить :к опциям, где мы ожидаем аргумент.
Aukhan

51

использование getopt

Почему Гетопт?

Чтобы проанализировать разработанные аргументы командной строки, чтобы избежать путаницы и уточнить параметры, которые мы анализируем, чтобы читатель команд мог понять, что происходит.

Что такое getopt?

getoptиспользуется для разбиения (разбора) параметров в командных строках для упрощения анализа с помощью процедур оболочки и для проверки допустимых параметров. Для этого он использует getopt(3)подпрограммы GNU .

getopt может иметь следующие типы опций.

  1. Варианты без значения
  2. параметры пары ключ-значение

Примечание. В этом документе при объяснении синтаксиса:

  • Все, что находится внутри [], является необязательным параметром в синтаксисе / примерах.
  • является заполнителем, что означает, что он должен быть заменен фактическим значением.

КАК ИСПОЛЬЗОВАТЬ getopt?

Синтаксис: первая форма

getopt optstring parameters

Примеры:

# This is correct
getopt "hv:t::" "-v 123 -t123"  
getopt "hv:t::" "-v123 -t123"  # -v and 123 doesn't have whitespace

# -h takes no value.
getopt "hv:t::" "-h -v123"


# This is wrong. after -t can't have whitespace.
# Only optional params cannot have whitespace between key and value
getopt "hv:t::" "-v 123 -t 123"

# Multiple arguments that takes value.
getopt "h:v:t::g::" "-h abc -v 123 -t21"

# Multiple arguments without value
# All of these are correct
getopt "hvt" "-htv"
getopt "hvt" "-h -t -v"
getopt "hvt" "-tv -h"

Здесь h, v, t - параметры, а -h -v -t - то, как параметры должны быть заданы в командной строке.

  1. «h» - это опция без значения.
  2. 'v:' подразумевает, что опция -v имеет значение и является обязательной. ':' означает, что имеет значение.
  3. 't ::' подразумевает, что опция -t имеет значение, но является необязательной. «::» означает необязательный.

В необязательном параметре значение не может иметь разделение пробелов с помощью опции. Так, в примере "-t123" -t это опция 123, это значение.

Синтаксис: вторая форма

getopt [getopt_options] [--] [optstring] [parameters]

Здесь после того, как Getopt делится на пять частей

  • Сама команда т.е. getopt
  • Getopt_options, он описывает, как анализировать аргументы. опции с одним штрихом, варианты с двойным штрихом.
  • -, отделяет getopt_options от параметров, которые вы хотите проанализировать, и разрешенных коротких параметров
  • Короткие варианты взяты сразу после - найден. Так же, как первый синтаксис формы.
  • Параметры, это те параметры, которые вы передали в программу. Параметры, которые вы хотите проанализировать и получить фактические значения, установленные для них.

Примеры

getopt -l "name:,version::,verbose" -- "n:v::V" "--name=Karthik -version=5.2 -verbose"

Синтаксис: третья форма

getopt [getopt_options] [-o options] [--] [optstring] [parameters]

Здесь после того, как Getopt делится на пять частей

  • Сама команда т.е. getopt
  • Getopt_options, он описывает, как анализировать аргументы. опции с одним штрихом, варианты с двойным штрихом.
  • Короткие опции, т.е. -o или --options. Точно так же, как первый синтаксис формы, но с опцией «-o» и перед «-» (двойная черта).
  • -, отделяет getopt_options от параметров, которые вы хотите проанализировать, и разрешенных коротких параметров
  • Параметры, это те параметры, которые вы передали в программу. Параметры, которые вы хотите проанализировать и получить фактические значения, установленные для них.

Примеры

getopt -l "name:,version::,verbose" -a -o "n:v::V" -- "-name=Karthik -version=5.2 -verbose"

GETOPT_OPTIONS

getopt_options изменяет способ анализа параметров командной строки.

Ниже приведены некоторые из getopt_options

Опция: -l или --longoptions

Означает, что команда getopt должна позволять распознавать многосимвольные параметры. Несколько параметров разделены запятой.

Например, --name=Karthikэто длинная опция, отправляемая в командной строке. В getopt использование длинных опций похоже на

getopt "name:,version" "--name=Karthik"

Поскольку имя: указано, опция должна содержать значение

Опция: -a или --alternative

Означает, что команда getopt должна позволять длинной опции иметь одну черту '-' вместо двойной тире '-'.

Пример, вместо того, чтобы --name=Karthikвы могли использовать просто-name=Karthik

getopt "name:,version" "-name=Karthik"

Полный пример скрипта с кодом:

#!/bin/bash

# filename: commandLine.sh
# author: @theBuzzyCoder

showHelp() {
# `cat << EOF` This means that cat should stop reading when EOF is detected
cat << EOF  
Usage: ./installer -v <espo-version> [-hrV]
Install Pre-requisites for EspoCRM with docker in Development mode

-h, -help,          --help                  Display help

-v, -espo-version,  --espo-version          Set and Download specific version of EspoCRM

-r, -rebuild,       --rebuild               Rebuild php vendor directory using composer and compiled css using grunt

-V, -verbose,       --verbose               Run script in verbose mode. Will print out each step of execution.

EOF
# EOF is found above and hence cat command stops reading. This is equivalent to echo but much neater when printing out.
}


export version=0
export verbose=0
export rebuilt=0

# $@ is all command line parameters passed to the script.
# -o is for short options like -v
# -l is for long options with double dash like --version
# the comma separates different long options
# -a is for long options with single dash like -version
options=$(getopt -l "help,version:,verbose,rebuild,dryrun" -o "hv:Vrd" -a -- "$@")

# set --:
# If no arguments follow this option, then the positional parameters are unset. Otherwise, the positional parameters 
# are set to the arguments, even if some of them begin with a ‘-’.
eval set -- "$options"

while true
do
case $1 in
-h|--help) 
    showHelp
    exit 0
    ;;
-v|--version) 
    shift
    export version=$1
    ;;
-V|--verbose)
    export verbose=1
    set -xv  # Set xtrace and verbose mode.
    ;;
-r|--rebuild)
    export rebuild=1
    ;;
--)
    shift
    break;;
esac
shift
done

Запуск этого файла сценария:

# With short options grouped together and long option
# With double dash '--version'

bash commandLine.sh --version=1.0 -rV
# With short options grouped together and long option
# With single dash '-version'

bash commandLine.sh -version=1.0 -rV

# OR with short option that takes value, value separated by whitespace
# by key

bash commandLine.sh -v 1.0 -rV

# OR with short option that takes value, value without whitespace
# separation from key.

bash commandLine.sh -v1.0 -rV

# OR Separating individual short options

bash commandLine.sh -v1.0 -r -V


getopt vs getopts .. совсем другое межплатформенное соответствие
shadowbq

35

Пример, упакованный с getopt(мой дистрибутив вставил его /usr/share/getopt/getopt-parse.bash) выглядит так, как будто он охватывает все ваши случаи:

#!/bin/bash

# A small example program for using the new getopt(1) program.
# This program will only work with bash(1)
# An similar program using the tcsh(1) script language can be found
# as parse.tcsh

# Example input and output (from the bash prompt):
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
# Option a
# Option c, no argument
# Option c, argument `more'
# Option b, argument ` very long '
# Remaining arguments:
# --> `par1'
# --> `another arg'
# --> `wow!*\?'

# Note that we use `"$@"' to let each command-line parameter expand to a 
# separate word. The quotes around `$@' are essential!
# We need TEMP as the `eval set --' would nuke the return value of getopt.
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n 'example.bash' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

while true ; do
    case "$1" in
        -a|--a-long) echo "Option a" ; shift ;;
        -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
        -c|--c-long) 
            # c has an optional argument. As we are in quoted mode,
            # an empty parameter will be generated if its optional
            # argument is not found.
            case "$2" in
                "") echo "Option c, no argument"; shift 2 ;;
                *)  echo "Option c, argument \`$2'" ; shift 2 ;;
            esac ;;
        --) shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done
echo "Remaining arguments:"
for arg do echo '--> '"\`$arg'" ; done

11
Внешнюю команду getopt (1) никогда нельзя использовать безопасно, если только вы не знаете, что это GNU getopt, вы вызываете ее специфичным для GNU способом и убедитесь, что GETOPT_COMPATIBLE отсутствует в среде. Вместо этого используйте getopts (встроенную оболочку) или просто переключайтесь между позиционными параметрами.
Жиль Квено

@sputnick, tyvm, не знал этого.
Брайан Каин

14
Эх, ни одна внешняя команда не является безопасной для использования по этому стандарту. Во встроенных getopts отсутствуют важные функции, и если вы хотите проверить GETOPT_COMPATIBLE, это проще, чем портировать функции getopt.
Майкл Терри

12

Я знаю, что на этот вопрос уже дан ответ, но для протокола и для тех, кто имеет те же требования, что и я, я решил опубликовать этот связанный ответ. Код заполнен комментариями для объяснения кода.

Обновленный ответ:

Сохраните файл как getopt.sh:

#!/bin/bash

function get_variable_name_for_option {
    local OPT_DESC=${1}
    local OPTION=${2}
    local VAR=$(echo ${OPT_DESC} | sed -e "s/.*\[\?-${OPTION} \([A-Z_]\+\).*/\1/g" -e "s/.*\[\?-\(${OPTION}\).*/\1FLAG/g")

    if [[ "${VAR}" == "${1}" ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

function parse_options {
    local OPT_DESC=${1}
    local INPUT=$(get_input_for_getopts "${OPT_DESC}")

    shift
    while getopts ${INPUT} OPTION ${@};
    do
        [ ${OPTION} == "?" ] && usage
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
            [ "${VARNAME}" != "" ] && eval "${VARNAME}=${OPTARG:-true}" # && printf "\t%s\n" "* Declaring ${VARNAME}=${!VARNAME} -- OPTIONS='$OPTION'"
    done

    check_for_required "${OPT_DESC}"

}

function check_for_required {
    local OPT_DESC=${1}
    local REQUIRED=$(get_required "${OPT_DESC}" | sed -e "s/\://g")
    while test -n "${REQUIRED}"; do
        OPTION=${REQUIRED:0:1}
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
                [ -z "${!VARNAME}" ] && printf "ERROR: %s\n" "Option -${OPTION} must been set." && usage
        REQUIRED=${REQUIRED:1}
    done
}

function get_input_for_getopts {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_optional {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/[^[]*\(\[[^]]*\]\)[^[]*/\1/g" -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_required {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/\[[^[]*\]//g" -e "s/[][ -]//g"
}

function usage {
    printf "Usage:\n\t%s\n" "${0} ${OPT_DESC}"
    exit 10
}

Тогда вы можете использовать это так:

#!/bin/bash
#
# [ and ] defines optional arguments
#

# location to getopts.sh file
source ./getopt.sh
USAGE="-u USER -d DATABASE -p PASS -s SID [ -a START_DATE_TIME ]"
parse_options "${USAGE}" ${@}

echo ${USER}
echo ${START_DATE_TIME}

Старый ответ:

Недавно мне нужно было использовать общий подход. Я столкнулся с этим решением:

#!/bin/bash
# Option Description:
# -------------------
#
# Option description is based on getopts bash builtin. The description adds a variable name feature to be used
# on future checks for required or optional values.
# The option description adds "=>VARIABLE_NAME" string. Variable name should be UPPERCASE. Valid characters
# are [A-Z_]*.
#
# A option description example:
#   OPT_DESC="a:=>A_VARIABLE|b:=>B_VARIABLE|c=>C_VARIABLE"
#
# -a option will require a value (the colon means that) and should be saved in variable A_VARIABLE.
# "|" is used to separate options description.
# -b option rule applies the same as -a.
# -c option doesn't require a value (the colon absense means that) and its existence should be set in C_VARIABLE
#
#   ~$ echo get_options ${OPT_DESC}
#   a:b:c
#   ~$
#


# Required options 
REQUIRED_DESC="a:=>REQ_A_VAR_VALUE|B:=>REQ_B_VAR_VALUE|c=>REQ_C_VAR_FLAG"

# Optional options (duh)
OPTIONAL_DESC="P:=>OPT_P_VAR_VALUE|r=>OPT_R_VAR_FLAG"

function usage {
    IFS="|"
    printf "%s" ${0}
    for i in ${REQUIRED_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
    printf " %s" "-${i:0:1} $VARNAME"
    done

    for i in ${OPTIONAL_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
        printf " %s" "[-${i:0:1} $VARNAME]"
    done
    printf "\n"
    unset IFS
    exit
}

# Auxiliary function that returns options characters to be passed
# into 'getopts' from a option description.
# Arguments:
#   $1: The options description (SEE TOP)
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   OPTIONS=$(get_options ${OPT_DESC})
#   echo "${OPTIONS}"
#
# Output:
#   "h:f:PW"
function get_options {
    echo ${1} | sed -e "s/\([a-zA-Z]\:\?\)=>[A-Z_]*|\?/\1/g"
}

# Auxiliary function that returns all variable names separated by '|'
# Arguments:
#       $1: The options description (SEE TOP)
#
# Example:
#       OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#       VARNAMES=$(get_values ${OPT_DESC})
#       echo "${VARNAMES}"
#
# Output:
#       "H_VAR|F_VAR|P_VAR|W_VAR"
function get_variables {
    echo ${1} | sed -e "s/[a-zA-Z]\:\?=>\([^|]*\)/\1/g"
}

# Auxiliary function that returns the variable name based on the
# option passed by.
# Arguments:
#   $1: The options description (SEE TOP)
#   $2: The option which the variable name wants to be retrieved
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   H_VAR=$(get_variable_name ${OPT_DESC} "h")
#   echo "${H_VAR}"
#
# Output:
#   "H_VAR"
function get_variable_name {
    VAR=$(echo ${1} | sed -e "s/.*${2}\:\?=>\([^|]*\).*/\1/g")
    if [[ ${VAR} == ${1} ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

# Gets the required options from the required description
REQUIRED=$(get_options ${REQUIRED_DESC})

# Gets the optional options (duh) from the optional description
OPTIONAL=$(get_options ${OPTIONAL_DESC})

# or... $(get_options "${OPTIONAL_DESC}|${REQUIRED_DESC}")

# The colon at starts instructs getopts to remain silent
while getopts ":${REQUIRED}${OPTIONAL}" OPTION
do
    [[ ${OPTION} == ":" ]] && usage
    VAR=$(get_variable_name "${REQUIRED_DESC}|${OPTIONAL_DESC}" ${OPTION})
    [[ -n ${VAR} ]] && eval "$VAR=${OPTARG}"
done

shift $(($OPTIND - 1))

# Checks for required options. Report an error and exits if
# required options are missing.

# Using function version ...
VARS=$(get_variables ${REQUIRED_DESC})
IFS="|"
for VARNAME in $VARS;
do
    [[ -v ${VARNAME} ]] || usage
done
unset IFS

# ... or using IFS Version (no function)
OLDIFS=${IFS}
IFS="|"
for i in ${REQUIRED_DESC};
do
    VARNAME=$(echo $i | sed -e "s/.*=>//g")
    [[ -v ${VARNAME} ]] || usage
    printf "%s %s %s\n" "-${i:0:1}" "${!VARNAME:=present}" "${VARNAME}"
done
IFS=${OLDIFS}

Я не тестировал это грубо, поэтому у меня могли быть некоторые ошибки там.


1
Если вы используете getoptsфункцию, добавьте local OPTIND OPTARGк функции
Гленн Джекман

@glennjackman на самом деле это больше похоже на подход sed, чем на использованиеgetopts
Себастьян

8

POSIX 7 пример

Также стоит проверить пример из стандарта: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)   printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"

И тогда мы можем попробовать это:

$ sh a.sh
Remaining arguments are: 
$ sh a.sh -a
Option -a specified
Remaining arguments are: 
$ sh a.sh -b
No arg for -b option
Usage: a.sh: [-a] [-b value] args
$ sh a.sh -b myval
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh -a -b myval
Option -a specified
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh remain
Remaining arguments are: remain
$ sh a.sh -- -a remain
Remaining arguments are: -a remain

Протестировано в Ubuntu 17.10, shтире 0.5.8.


0

«getops» и «getopt» очень ограничены. Хотя «getopt» предлагается вообще не использовать, он предлагает длинные варианты. Где в качестве "getopts" разрешены только односимвольные опции, такие как "-a", "-b". Есть несколько недостатков при использовании любого из них.

Итак, я написал небольшой скрипт, который заменяет «getopts» и «getopt». Это начало, возможно, его можно улучшить.

Обновление 08-04-2020 : я добавил поддержку дефисов, например, "--package-name".

Использование: "./script.sh пакет установить --package" имя с пробелом "--build --archive"

# Example:
# parseArguments "${@}"
# echo "${ARG_0}" -> package
# echo "${ARG_1}" -> install
# echo "${ARG_PACKAGE}" -> "name with space"
# echo "${ARG_BUILD}" -> 1 (true)
# echo "${ARG_ARCHIVE}" -> 1 (true)
function parseArguments() {
  PREVIOUS_ITEM=''
  COUNT=0
  for CURRENT_ITEM in "${@}"
  do
    if [[ ${CURRENT_ITEM} == "--"* ]]; then
      printf -v "ARG_$(formatArgument "${CURRENT_ITEM}")" "%s" "1" # could set this to empty string and check with [ -z "${ARG_ITEM-x}" ] if it's set, but empty.
    else
      if [[ $PREVIOUS_ITEM == "--"* ]]; then
        printf -v "ARG_$(formatArgument "${PREVIOUS_ITEM}")" "%s" "${CURRENT_ITEM}"
      else
        printf -v "ARG_${COUNT}" "%s" "${CURRENT_ITEM}"
      fi
    fi

    PREVIOUS_ITEM="${CURRENT_ITEM}"
    (( COUNT++ ))
  done
}

# Format argument.
function formatArgument() {
  ARGUMENT="${1^^}" # Capitalize.
  ARGUMENT="${ARGUMENT/--/}" # Remove "--".
  ARGUMENT="${ARGUMENT//-/_}" # Replace "-" with "_".
  echo "${ARGUMENT}"
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.