Como posso detectar que nenhuma opção foi passada com getopts?

19

Eu tenho esse código -

#getoptDemo.sh
usage()
{
    echo "usage: <command> options:<w|l|h>"
}
while getopts wlh: option
do
    case $option in
            (w)
                    name='1';;
            (l)
                    name='2';;
            (h)
                    name='3';;
            (*)
                    usage
                    exit;;
    esac
done
print 'hi'$name

Quando executo bash getoptDemos.sh(sem a opção), ela é impressa em hivez de chamar a função usage. Ele chama uso quando outras opções além de w, he são fornecidas. Então não pode funcionar quando nenhuma opção é especificada.

Eu tentei usar ?, \?, :no lugar de *mas eu não posso conseguir o que eu queria. Quero dizer toda a docssobre getoptdiz-lo para usar ?.

O que estou fazendo de errado?

Hussain Tamboli
fonte
Em que shell você o está executando?
Julian
Estou executando-lo sob/bin/bash
Hussain Tamboli

Respostas:

15

Quando você executa esse script sem nenhuma opção, o getopt retornará false, portanto não entrará no loop. Irá apenas aparecer na impressão - é este ksh / zsh?

Se você precisa ter uma opção, é melhor apostar em testar o nome $ após o loop.

if [ -z "$name" ]
then
   usage
   exit
fi

Mas verifique se ele $nameestava vazio antes de chamar getopts(pois pode haver um $nameambiente no qual o shell recebeu na inicialização) com

unset name

(antes do getoptsloop)

Julian
fonte
não. sua festança. Então, como faço para conseguir o que quero - manipular a no argumentcondição usando o bash.
Hussain Tamboli
Se você quer uma opção obrigatória, acho que isso não é possível - provavelmente é por isso que elas são chamadas de opções :) Você pode testar o $ name, após o loop, para ter certeza de que foi definido. if [-z "$ name"]; então uso; Saída ; fi
Julian
obrigado. o código acima realmente ajudou. É muito ruim getoptsnão ter essa disposição. O que é pior do que isso é, não pode te ajudar.
Hussain Tamboli
e se eu estivesse executando outro shell? zsh, sh. algum shell que não seja o bash cuida dessa condição?
Hussain Tamboli
20

getoptsprocessa as opções por sua vez. Esse é o seu trabalho. Se o usuário não passar nenhuma opção, a primeira chamada de getoptssai do loop while.

Se nenhuma das opções aceitar um argumento, o valor de OPTINDindica quantas opções foram passadas. Em geral, OPTINDé o número de argumentos que são opções ou argumentos para opções, em oposição a argumentos não opcionais (operandos).

while getopts …; do …; done
if [ $OPTIND -eq 1 ]; then echo "No options were passed"; fi
shift $((OPTIND-1))
echo "$# non-option arguments"

De qualquer forma, você não está tentando determinar se não havia opções, mas se nenhuma das nameopções de configuração foi aprovada. Portanto, verifique se nameestá desabilitado (e desative-o primeiro) .

Gilles 'SO- parar de ser mau'
fonte
11
obrigado. +1 para você. quando eu coloco as últimas 3 linhas do seu código em sample.sh e corro bash sample.sh -abc file.txtisso dá - 1 non-option arguments. como descubro quantas opções foram dadas. (aqui 3)
Hussain Tamboli
11
Estranho, ninguém comentou esse pequeno bug - se nenhuma opção for dada, OPTIND será 1 depois do loop 'while getopts ...'. Portanto, a verificação if deve verificar a igualdade com 1, e não com zero.
Daniel
4

Se seu script precisar receber argumentos de opção, for esse o caso, coloque esse bloco no início (antes de getops).

if [[ ! $@ =~ ^\-.+ ]]
then
  #display_help;
fi

O bloco verifica se a sequência de parâmetros não começa com o -símbolo, o que indica que o primeiro parâmetro não é um argumento de opção.

Mcounad
fonte
2

Eu verificaria isso com uma variável. Se getopts nunca passa no loop em caso de nenhum argumento, você pode usá-lo, por exemplo, como este:

#getoptDemo.sh
usage()
{
    echo "usage: <command> options:<w|l|h>"
}

no_args="true"
while getopts wlh: option
do
    case $option in
            (w)
                    name='1';;
            (l)
                    name='2';;
            (h)
                    name='3';;
            (*)
                    usage
                    exit;;
    esac
    no_args="false"
done

[[ "$no_args" == "true" ]] && { usage; exit 1; }

print 'hi'$name
Imre Kneifel
fonte
0

Pouco antes do seu getoptsbloco, verifique se $1(o primeiro argumento / opção que você passou na linha de comando) é igual a uma string vazia. Se for, imprima a mensagem de uso e saia (ou execute algumas funções "sem opções" se você é anarquista); caso contrário, deixe-as getoptsanalisar as opções normalmente.

A razão pela qual esse recurso não está incluído no getopts é porque você já pode realizá-lo no bash com um "if-else". Exemplo:

if [[ $1 == "" ]]; then
    Your_Usage_Function;
    exit;
else
   #parse options with getopts code block here;
fi

Faz sentido?

codrcodz
fonte