Como lidar com opções em um script de shell?

17

Existem algumas ferramentas internas que reconhecerão -xe --xxxxcomo opções, e não argumentos, ou você precisará passar por todas as variáveis ​​de entrada, testar traços e analisar os argumentos a seguir?

user394
fonte

Respostas:

16

Use getopts.

É bastante portátil, como na especificação POSIX. Infelizmente, ele não suporta opções longas.

Veja também este getopts tutorial, cortesia do wiki do bash-hackers e esta pergunta do stackoverflow.

Se você precisar apenas de opções curtas, o padrão de uso típico para getopts(usando relatórios de erro não silenciosos) é:

# 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
fonte
3
Deve-se mencionar que getoptsempre deve ser verificado como GNU getopt antes de você usá-lo, mas você não deve usá-lo de qualquer maneira, pois getoptsé mais portátil (e geralmente melhor) de qualquer maneira. Se você fazer necessidade de chamá-lo por algum motivo, chamá-lo de uma forma específica de GNU, e garantir que GETOPT_COMPATIBLEnão está no ambiente.
Chris Baixo
O que o cólon while getopts "ab:" optfaz?
user394
11
@ user394 A :letra de opção após indica que requer um argumento. A :como o primeiro caractere significa suprimir as mensagens de erro.
Jw013 23/10/19
22

Presumo que você esteja usando o bash ou similar. Um exemplo:

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[@]}" "$@"
Glenn Jackman
fonte
11
+1 para usar +=com uma matriz. Não sabia que você poderia fazer isso. Agradável!
James Sneeringer 14/10
5

Você precisa escrever um ciclo para analisar os parâmetros. Na verdade, você pode usar o getoptscomando para fazê-lo facilmente.

Este é um exemplo simples da getoptspágina de manual:

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
fonte
2

Escrevi recentemente um script para um trabalho versátil e que permitia vários tipos de comutadores em qualquer ordem. Não posso divulgar o script completo por razões legais óbvias (sem mencionar que não o tenho comigo no momento), mas aqui está o ponto principal. Você pode colocá-lo em uma sub-rotina e chamá-lo no final do seu script:

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

Eu recomendo limitar seu script bash a menos de 400 linhas / 15k caracteres; meu roteiro mencionado cresceu além desse tamanho e ficou muito difícil de trabalhar. Comecei a reescrevê-lo no Perl, que é muito mais adequado para a tarefa. Tenha isso em mente ao trabalhar em seus scripts no bash. O Bash é ótimo para pequenos scripts e oneliners, mas qualquer coisa mais complexa e é melhor escrevê-lo em Perl.

Observe que eu não testei o exposto acima, então provavelmente não funciona, mas você obtém a idéia geral.

laebshade
fonte
O modo como você liga optionsno final não está correto, ele retornará -bash: syntax error near unexpected token $@. Chame como options "$@".
18711 Chris Down
Sim, eu misturei Perl e Bash. Corrigido.
Laebshade 19/09/11
Essa whilecondição não deveria ser em (($#))vez disso?
manatwork
Por que você precisaria de dois conjuntos de parênteses $#? Edit: você está certo. Corrigidowhile (( "$#" ))
laebshade 20/09/11