Как мне работать с переключателями в сценарии оболочки?

17

Существуют ли какие-либо встроенные инструменты, которые распознают -xи --xxxxкак переключатели, а не аргументы, или вам нужно пройти через все входные переменные, проверить на наличие тире, а затем проанализировать аргументы?

user394
источник

Ответы:

16

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

Это довольно переносимо, как в спецификации POSIX. К сожалению, он не поддерживает длинные варианты.

Смотрите также этот getopts учебник, предоставленный вики bash-hackers, и этот вопрос от stackoverflow.

Если вам нужны только короткие варианты, типичный шаблон использования getopts(с использованием не-тихой отчетности об ошибках):

# process arguments "$1", "$2", ... (i.e. "$@")
while getopts "ab:" opt; do
    case $opt in
    a) aflag=true ;; # Handle -a
    b) barg=$OPTARG ;; # Handle -b argument
    \?) ;; # Handle error: unknown option or missing required argument.
    esac
done
jw013
источник
3
Следует отметить, что getoptперед использованием его всегда следует проверять как GNU getopt, но вы все равно не должны его использовать, так как в любом случае getoptsон более переносим (и, как правило, лучше). Если вы делаете потребность назвать его по какой - то причине, называют его в GNU-определенным образом, и убедитесь , что GETOPT_COMPATIBLEэто не в окружающей среде.
Крис Даун
Что делает толстая кишка while getopts "ab:" opt?
user394
1
@ user394 Буква :после опции указывает, что она требует аргумента. A :в качестве первого символа означает подавление сообщений об ошибках.
jw013
22

Я предполагаю, что вы используете Bash или подобное. Пример:

all=false
long=false

while getopts ":hal" option; do
  case $option in
    h) echo "usage: $0 [-h] [-a] [-l] file ..."; exit ;;
    a) all=true ;;
    l) long=true ;;
    ?) echo "error: option -$OPTARG is not implemented"; exit ;;
  esac
done

# remove the options from the positional parameters
shift $(( OPTIND - 1 ))

ls_opts=()
$all && ls_opts+=( -a )
$long && ls_opts+=( -l )

# now, do it
ls "${ls_opts[@]}" "$@"
Гленн Джекман
источник
1
+1 за использование +=с массивом. Не знал, что ты мог сделать это. Ницца!
Джеймс Снирингер
5

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

Это простой пример со getoptsстраницы руководства:

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" "$*"
andcoz
источник
2

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

options () {

    if [ -n "$1" ]; then # test if any arguments passed - $1 will always exist
        while (( "$#" )); do  # process ALL arguments
            if [ "$1" = ^-t$ ]; then # -t short for "test"
                # do something here THEN shift the argument
                # shift removes it from $@ and reduces $# by
                # one if you supply no argument
                shift

            # we can also process multiple arguments at once
            elif [[ "$1" =~ ^--test=[:alnum:]{1,8}$ ]] && [[ "$2" =~ ^-t2$ ]] && [[ -n "$3" ]]; then # check for 3 arguments
                # do something more then remove them ALL from the arg list    
                shift 3
            else
                echo "No matching arguments!"
                echo "Usage: [script options list here]"
            fi
        done
    else
        echo "Usage: [script options list here]"
        exit 0
    fi
}

options "$@" # run options and loop through/process ALL arguments

Я рекомендую ограничить ваш bash скрипт до 400 строк / 15k символов; Мой вышеупомянутый сценарий вырос до этого размера и стало очень трудно работать. Я начал переписывать его на Perl, который гораздо лучше подходит для этой задачи. Имейте это в виду, работая над своими скриптами в bash. Bash отлично подходит для небольших сценариев и подписчиков, но для чего-то более сложного, и вам лучше написать его на Perl.

Обратите внимание, я не проверял вышеизложенное, поэтому, вероятно, это не сработало, но вы получили общее представление об этом.

laebshade
источник
То, как вы звоните optionsв конце, неверно, оно вернется -bash: syntax error near unexpected token $@. Назовите это как options "$@".
Крис Даун
Да, я перепутал Perl и Bash. Исправленный.
laebshade
Это whileусловие не должно быть (($#))вместо этого?
Манатворк
Зачем вам нужны два набора скобок для $#? Редактировать: вы правы. Исправлено этоwhile (( "$#" ))
laebshade