2016-07-11 8 views
1

Я написал сценарий в Bash, который служит в качестве шаблона для нескольких мониторов. Я выбираю getopt, чтобы иметь возможность использовать длинные опции в CLI. Однако у меня есть некоторые проблемы с его реализацией.Getopt не разбирается в bash

Весь сценарий намного дольше, но это соответствующая часть:

#!/bin/bash 
# 
# FUNCTION 
# main 
# DESCRIPTION 
# Main function. Everything will be called from here 
# ARGS 
# Nothing 
# 
# RETURN CODE 
# Nothing 
# 
# Main function. Everything will be called from here 
main() { 
    # Parse the options and arguments 
    parse_options "${@}" 

    # Check if the interval is set to a valid number 
    check_interval 
} 

# 
# FUNCTION 
# check_interval 
# DESCRIPTION 
# Checks if a number is valid 
# ARGS 
# 1: number to be checked 
# RETURN CODE 
# 0: valid 
# 1: invalid 
# 
check_interval() { 
    # We don't have to worry if interval is set at all, because getopt is already doing this 
    if (! check_number_pos ${arginterval}); then 
    echo "Error: invalid interval: ${arginterval}" 
    show_usage 
    exit 2 
    fi 
} 

# 
# FUNCTION 
# show_usage 
# DESCRIPTION 
# This is the Usage section. We a showing the Usage according to docopt standards 
# ARGS 
# Nothing 
# RETURN CODE 
# Nothing 
# 
show_usage() { 
    echo "Usage:"                   >&2 
    echo " ${THIS_SCRIPT_NAME} -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]" >&2 
    echo " ${THIS_SCRIPT_NAME} [-h|--help]"            >&2 
} 

# 
# FUNCTION 
# check_number_pos 
# DESCRIPTION 
# Checks if a number is valid and positive 
# ARGS 
# 1: number to be checked 
# 
# RETURN CODE 
# 0: valid and positive 
# 1: invalid or negative 
# 
check_number_pos() { 
    local returnval 
    if [[ "${1}" =~ ^[0-9]+$ ]]; then 
    returnval=0 
    else 
    returnval=1 
    fi 
    return ${returnval} 
} 

# 
# FUNCTION 
# parse_options 
# DESCRIPTION 
# Parse options from command line 
# ARGS 
# @: Arguments and options as given at CLI 
# RETURN CODE 
# Nothing 
# 
parse_options() { 
    # Use getopt(1) to parse options according to POSIX. If it fails, an error is shown, and we're showing the Usage and exit 
    # Add new options here and also in the case-statement below. 
    # Short options must be added in the 'options'-section 
    # Long options must be added in the 'longoptions'-section 
    # All short options must have a long equivalent 
    # The --name is set so the error-output will not show 'getopt'-errors but neat <application name>-errors 
    # Options and longoptions have the following format: 
    # <letter>  Option without argument 
    # <letter>:  Option with mandarory argument 
    # <letter>::  Option with optional argument <- this is broken for short options and long options without '='. Don't use it! 
    local -r GETOPT=$(getopt --name ${0} --options hrvdi: --longoptions help,random,verbose,debug,colors,randomwait:,interval: -- "${@}") 

    if [ ${?} != 0 ]; then 
    echo "Error: Error while getting arguments" 
    show_usage 
    exit 127; 
    fi 

    # No options or arguments given. Show Usage. 
    if [[ "${GETOPT}" == " --" ]]; then 
    show_usage 
    exit 127; 
    fi 

    # Evaluate GETOPT. We need this to have the quotes in the output of getopt(1) interpreted. 
    eval set -- "${GETOPT}" 

    # Walk through all the options. Don't put too much code in here, just point to a function or set a variable. 
    # Please note, all new options need to be added here but also in the GETOPT line above. 
    # Note: shift removes the first value from the string, so the option itself will be removed from the GETOPT-string, and the argument is available in $1 
    # After using an argument, please shift again, so the next option will be the first value in GETOPT 
    while true; 
    do 
    case "${1}" in 
     -i|--interval) 
     shift 
     arginterval=${1} 
     shift 
     ;; 
     -r|--random) 
     shift 
     flagrandom=1 
     ;; 
     --randomwait) 
     shift 
     flagrandom=1 
     argrandom=${1} 
     shift 
     ;; 
     -v|-d|--verbose|--debug) 
     flagdebug=1 
     shift 
     ;; 
     --colors) 
     flagcolors=1 
     shift 
     ;; 
     -h|--help) 
     #show_help 
     exit 0 
     ;; 
     --) 
     shift 
     break 
     ;; 
     -*) 
    echo "Error: unrecognized option ${1}" 
     show_usage 
     exit 127 
     ;; 
     *) 
     show_usage 
     exit 127 
     ;; 
    esac 
    done 
} 

#Call main function after all 
main "${@}" 

Теперь, когда я звоню скрипт правильного пути, все идет гладко:

$ ./test1.sh -i 10 

Когда я забыть аргумент, он также делает то, что я хочу:

$ ./test1.sh -i 
./test1.sh: option requires an argument -- 'i' 
Usage: 
    -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors] 
    [-h|--help] 

Но когда я просто забываю аргумент и добавляю другой, он не:

$ ./test1.sh -i --colors 
Error: invalid interval: --colors 
Usage: 
    -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors] 
    [-h|--help] 

Это происходит потому, что я проверяю, если интервал является целым числом, но и для других целей, это опасная вещь Как я могу изменить случай, поэтому он не будет читать параметры в качестве аргументов ? До сих пор getopt не очень хорошо меня обслуживал, потому что я также столкнулся с другой ошибкой/функцией: «необязательный аргумент» (: :) не работает так, как я ожидал, поскольку он работает только при использовании с параметром «=» между опцией и аргумент.

Версии:

$ getopt -V 
getopt (enhanced) 1.1.4 
$ bash --version 
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu) 

ответ

2

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

while true; 
    do 
    case "${1}" in 
     -i|--interval) 
     shift 
     arginterval=${1} 
     if [[ $arginterval = -* ]]; then 
      printf 'You appear to have forgotten the interval argument before the %s option\n' "$arginterval" >&2 
      exit 1 
     fi 
     shift 
     ;; 

    ... 
+2

Действительно; аналогично, 'getup' требует, чтобы аргумент _optional_ option был привязан непосредственно к опции_ - без разделителя в короткой форме (например,' -i10'), с '=' в качестве разделителя в длинной форме (например, '- -interval = 10') - потому что это единственный _умножественный способ сделать это. – mklement0

+0

Хорошо, может быть, я пытаюсь заставить думать слишком глупо, но я ожидал, что getopt поймет, что --colors - это «вариант», потому что я ему сказал. Я добавлю еще несколько проверок скрипту, чтобы предотвратить ошибочный ввод. –

+0

'--colors' может быть вариантом, но нельзя сказать' getopt', что он также не может быть допустимым аргументом '-i'. – chepner

 Смежные вопросы

  • Нет связанных вопросов^_^