Como eu manejo interruptores em um script de shell?

15

Existem algumas ferramentas internas que reconhecerão -x e --xxxx como opções, e não argumentos, ou você precisará percorrer todas as variáveis de entrada, testar os travessões e depois analisar os argumentos a partir de então?

    
por user394 18.09.2011 / 20:47

4 respostas

14

Use getopts .

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

Veja também este tutorial getopts cortesia do wiki bash-hackers e este question de stackoverflow.

Se você precisar apenas de opções curtas, o padrão de uso típico para getopts (usando relatórios de erros 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
    
por 18.09.2011 / 21:24
20

Eu suponho que você esteja usando 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[@]}" "$@"
    
por 14.10.2011 / 22:54
5

Você tem que escrever um ciclo para analisar os parâmetros. Na verdade, você pode usar o comando getopts para fazer isso facilmente.

Este é um exemplo simples da página de manual 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" "$*"
    
por 18.09.2011 / 21:24
2

Eu escrevi um script recentemente para um trabalho que era versátil e permitia vários tipos de switches em qualquer ordem. Eu não posso divulgar o roteiro completo por razões óbvias legais (para não mencionar que eu não tenho isso comigo no momento), mas aqui está a carne dele .. 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 script acima mencionado ultrapassou esse tamanho e tornou-se muito difícil de trabalhar. Eu comecei a reescrevê-lo em Perl, que é muito mais adequado para a tarefa. Tenha isso em mente enquanto você trabalha em seus scripts no bash. O Bash é ótimo para pequenos scripts e oneliners, mas nada mais complexo e é melhor escrevê-lo em Perl.

Note que eu não testei o código acima, então provavelmente ele não funciona, mas você tem uma idéia geral dele.

    
por 18.09.2011 / 21:58