Перехват недопустимых параметров getopt

#bash #shell #case #getopt

#bash #оболочка #случай #getopt

Вопрос:

Я использую getopt (не getops ), чтобы предоставить моему сценарию bash возможность обрабатывать параметры и переключатели (как длинные —option, так и короткие -o формы).

Я хотел бы иметь возможность перехватывать недопустимые параметры и обрабатывать их, обычно повторяя, что пользователь должен попробовать cmd --help , а затем выйти из скрипта.

Дело в том, что недопустимые параметры перехватываются getopt, который сам выводит сообщение, такое как «getopt: недопустимый параметр — ‘x'»

Вот шаблон, который я использую для настройки параметров getopt:

 set -- $(getopt -o $SHORT_OPTIONS -l $LONG_OPTIONS -- "$@")
  

где как $ LONG_OPTIONS, так и $ SHORT_OPTIONS представляют собой список параметров, разделенных запятыми.

Вот как я обрабатываю обработку параметров:

  while [ $# -gt 0 ]
    do
        case "$1" in
            -h|--help)
                cat <<END_HELP_OUTPUT

    Help
    ----

    Usage: ./cmd.sh 

    END_HELP_OUTPUT

                shift;
                exit
                ;;
            --opt1)
                FLAG1=true
                shift
                ;;
            --opt2)
                FLAG2=true
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Option $1 is not a valid option."
                echo "Try './cmd.sh --help for more information."
                shift
                exit
                ;;
        esac
    done
  

getopt -q будет подавлять вывод, но моя схема захвата в case инструкции по-прежнему не выполняет то, что я ожидаю. Вместо этого программа просто выполняется, несмотря на недопустимые аргументы.

Ответ №1:

Такой стиль работает для меня:

 params="$(getopt -o d:h -l diff:,help --name "$cmdname" -- "$@")"

if [ $? -ne 0 ]
then
    usage
fi

eval set -- "$params"
unset params

while true
do
    case $1 in
        -d|--diff)
            diff_exec=(${2-})
            shift 2
            ;;
        -h|--help)
            usage
            exit
            ;;
        --)
            shift
            break
            ;;
        *)
            usage
            ;;
    esac
done
  

Комментарии:

1. В каком случае достигается *)?

2. @jarno Если у вас когда-либо возникнет несоответствие между вашим case оператором и getopt вызовом, оно будет перехвачено там. Это просто защитное программирование.

Ответ №2:

Это не самое надежное решение, но оно разумно; оно основывается на следующем:

  • Выводимое сообщение об ошибке getopt имеет префикс «getopt: «.
  • Предполагается, что допустимо пропускать очищенную версию сообщения об getopt ошибке, дополненную пользовательской информацией.

Фрагмент кода:

 # Invoke getopt; suppress its stderr initially.
args=$(getopt -o $SHORT_OPTIONS -l $LONG_OPTIONS -- "$@" 2>/dev/null)
if [[ $? -ne 0 ]]; then # getopt reported failure
    # Rerun the same getopt command so we can capture stderr output *only* this time.
    # Inefficient (and a potential maintenance headache, if literals were involved), but this will only execute in case of invalid input.
    # Alternatively, redirect the first getopt invocation's stderr output to a temp. file and read it here.
    errmsg=$(getopt -o $SHORT_OPTIONS -l $LONG_OPTIONS -- "$@" 2>amp;1 1>amp;-)
    # Strip getopt's prefix and augment with custom information.
    echo -e "${errmsg#getopt: }nTry './cmd.sh --help for more information." 1>amp;2
    exit 1
fi
  

Комментарии:

1. @TomAuger: Если вы считаете, что «довольно хрупкий» адекватно перефразирует «не самый надежный, но разумный», вы, должно быть, используете несовместимую англоязычную оболочку. Неизменно, попытка надежно извлечь информацию getopt , которая не была предназначена для программного отчета, будет сложной. Тем не менее, часть о предположении, что вывод stderr идет первым, действительно шаткая, поэтому я пересмотрел код, чтобы решить эту проблему. Если вы знаете, как преобразовать stdout и stderr в отдельные переменные при выполнении одной команды (не включая явное создание временных файлов), дайте мне знать.

Ответ №3:

Нужно ли вам вообще использовать getopt? Если вы просто используете

 while [ $# -gt 0 ]; do
  case "$1" in
    -d|--diff)
       diff_exec=(${2-})
       shift
       ;;
    -h|--help)
       usage
       exit
       ;;
     --)
       break
       ;;
     *)
       usage
       ;;
    esac
    shift
done
  

Тогда ваш собственный код выполняет проверку.

Ответ №4:

Я обнаружил, что это работает как последний элемент в инструкции getopts case:

*) eval echo «Нераспознанный аргумент $$[OPTIND-1]»; использование; выход ;;

Ответ №5:

Я не уверен, что это может помочь, но getopt(1) использует getopt(3), и, если я правильно помню, getopt(3) подавляет сообщение об ошибке, если первым символом OPTSTRING является двоеточие.

Комментарии:

1. Использование ‘:’ в качестве первого символа в optstring подавляет вывод сообщения. Однако код возврата из getopt(1) по-прежнему отличен от нуля, и нераспознанный параметр не выводится getopt(1).

Ответ №6:

Вот синтаксический анализ командной строки, который я использовал. Это можно было бы улучшить с помощью дополнительной логики синтаксического анализа для обработки отсутствующих параметров и параметров.

Для командной строки: -a AA -b BB -c CC, результат s / b a= AA b= BB c= CC

 OPT=( "$@" )  # Parses the command line into words.

for [[ I=0;I<${#OPT[@]};I   ]]  
   do
      case "${OPT[$I]}" in         
         -a) a=${OPT[$I 1]} ;;         
         -b) b=${OPT[$I 1]} ;;         
         -c) c=${OPT[$I 1]} ;;    
      esac
  done