Verifique o número de argumentos transmitidos para um script Bash

727

Gostaria que meu script Bash imprimisse uma mensagem de erro se a contagem de argumentos necessária não fosse atendida.

Eu tentei o seguinte código:

#!/bin/bash
echo Script name: $0
echo $# arguments 
if [$# -ne 1]; 
    then echo "illegal number of parameters"
fi

Por algum motivo desconhecido, tenho o seguinte erro:

test: line 4: [2: command not found

O que estou fazendo errado?

Naftaly
fonte
54
Você não deve nomear seu script test. Esse é o nome de um comando padrão do Unix, você não gostaria de escondê-lo.
Barmar 02/02
20
Sempre use espaços em torno de '[' ( '[[') ou '(' ( '((') no caso declarações em Bash.
zoska
5
Para adicionar ao comentário @zoska, você precisa de um espaço antes [porque ele é implementado como um comando, tente 'what ['.
Daniel Da Cunha
1
melhor exemplo é dado no link abaixo: stackoverflow.com/questions/4341630/...
Ramkrishna
3
@ Barmar certamente nomear isso testé bom, desde que não esteja no PATH?
user253751

Respostas:

1099

Assim como qualquer outro comando simples, [ ... ]ou testrequer espaços entre seus argumentos.

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
fi

Ou

if test "$#" -ne 1; then
    echo "Illegal number of parameters"
fi

Sugestões

Quando [[ ]]estiver no Bash, prefira usar , pois não faz a divisão de palavras e a expansão do nome do caminho para suas variáveis, que a citação pode não ser necessária, a menos que faça parte de uma expressão.

[[ $# -ne 1 ]]

Ele também possui outros recursos, como agrupamento de condições sem aspas, correspondência de padrões (correspondência de padrões estendida com extglob) e correspondência de expressões regulares.

O exemplo a seguir verifica se os argumentos são válidos. Permite um ou dois argumentos.

[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]

Para expressões aritméticas puras, utilizando (( ))para alguns ainda pode ser melhor, mas eles ainda são possíveis [[ ]]com seus operadores aritméticos como -eq, -ne, -lt, -le, -gt, ou -gecolocando a expressão como um único argumento string:

A=1
[[ 'A + 1' -eq 2 ]] && echo true  ## Prints true.

Isso deve ser útil se você também precisar combiná-lo com outros recursos [[ ]].

Saindo do script

Também é lógico fazer com que o script saia quando parâmetros inválidos são passados ​​para ele. Isso já foi sugerido nos comentários por ekangas, mas alguém editou esta resposta para tê-la com -1o valor retornado, então é melhor fazê-lo corretamente.

-1embora aceito por Bash como argumento para exitnão esteja explicitamente documentado e não seja adequado para ser usado como sugestão comum. 64também é o valor mais formal, pois é definido sysexits.hcom #define EX_USAGE 64 /* command line usage error */. A maioria das ferramentas como lstambém retorna 2com argumentos inválidos. Eu também costumava retornar 2meus scripts, mas ultimamente não me importava mais e simplesmente usava 1em todos os erros. Mas vamos colocar 2aqui, já que é mais comum e provavelmente não é específico do sistema operacional.

if [[ $# -ne 1 ]]; then
    echo "Illegal number of parameters"
    exit 2
fi

Referências

konsolebox
fonte
2
OP: Lembre-se de que [é apenas mais um comando, ou seja, tente which [.
Leo
5
Os comandos do @Leo podem ser integrados e não podem. No bash, [é um builtin, enquanto [[é uma palavra-chave. Em algumas conchas mais antigas, [nem sequer está embutido. Comandos como [naturalmente coexistem como um comando externo na maioria dos sistemas, mas comandos internos são priorizados pelo shell, a menos que você ignore com commandou exec. Verifique a documentação do shell sobre como eles avaliam. Observe a diferença e como eles podem se comportar de maneira diferente em cada concha.
Konsolebox #
78

Pode ser uma boa ideia usar expressões aritméticas se você estiver lidando com números.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi
Aleks-Daniel Jakimenko-A.
fonte
Por que isso pode ser uma boa ideia, no presente caso? Considerando eficiência, portabilidade e outros problemas, não é melhor usar a sintaxe mais simples e universalmente compreendida, ou seja [ ... ], quando isso faz o trabalho bem e não são necessárias operações sofisticadas?
Max
As expansões aritméticas @Max $(( ))não são sofisticadas e devem ser implementadas por todos os shells POSIX. No entanto, a (( ))sintaxe (sem $) não faz parte dela. Se por algum motivo você é limitado, certamente pode usar [ ], mas lembre-se de que não deve usá-lo [[ ]]também. Espero que você entenda as armadilhas [ ]e as razões pelas quais esses recursos existem. Mas essa era uma pergunta do Bash, por isso estamos fornecendo respostas do Bash ( "Como regra geral, [[é usado para seqüências de caracteres e arquivos. Se você deseja comparar números, use uma ArithmeticExpression" ).
Aleks-Daniel Jakimenko-A.
40

Em [] :! =, =, == ... existem operadores de comparação de cadeias e -eq, -gt ... são binários aritméticos .

Eu usaria:

if [ "$#" != "1" ]; then

Ou:

if [ $# -eq 1 ]; then
jhvaras
fonte
9
==é realmente um recurso não documentado, que acontece a trabalhar com GNU test. Ele também acontece a trabalhar com o FreeBSD test, mas pode não funcionar em foo test . A única comparação padrão é =(apenas para sua informação).
Martin Tournoij
1
Está documentada na entrada do bash man: Quando os operadores == e! = São usados, a sequência à direita do operador é considerada um padrão e corresponde de acordo com as regras descritas abaixo em Correspondência de Padrão. Se a opção de concha nocasematch estiver ativada, a correspondência será realizada sem considerar o caso de caracteres alfabéticos. O valor de retorno é 0 se a sequência corresponder (==) ou não (! =) Ao padrão e 1 caso contrário. Qualquer parte do padrão pode ser citada para forçá-lo a corresponder como uma sequência.
Jhvaras
2
@jhvaras: Foi exatamente o que Carpetsmoker disse: pode funcionar em algumas implementações (e, de fato, funciona no Bash), mas não é compatível com POSIX . Por exemplo, ele irá falhar com dash: dash -c '[ 1 == 1 ]'. O POSIX apenas especifica =, e não ==.
Gnourf_gniourf
34

Se você estiver interessado apenas em resgatar se um argumento em particular estiver ausente, a Substituição de parâmetros é excelente:

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT
Pat
fonte
Isso não é carregado de basismos?
Dwight Spencer
@DwightSpencer Isso importaria?
konsolebox
@Temak Posso, se você tiver perguntas específicas, mas o artigo vinculado a ele explica melhor do que eu.
Pat
13

Um liner simples que funciona pode ser feito usando:

[ "$#" -ne 1 ] && ( usage && exit 1 ) || main

Isso se divide em:

  1. teste a variável bash para o tamanho dos parâmetros $ # não é igual a 1 (nosso número de subcomandos)
  2. se verdadeiro, chame a função use () e saia com o status 1
  3. caso contrário, chame a função main ()

Pensa em observar:

  • use () pode ser apenas um eco simples "$ 0: params"
  • main pode ser um script longo
Dwight Spencer
fonte
1
Se você tiver outro conjunto de linhas após essa linha, isso seria errado, pois exit 1só se aplicaria ao contexto do subshell, tornando-o apenas sinônimo ( usage; false ). Eu não sou fã dessa maneira de simplificação quando se trata de análise de opções, mas você pode usar { usage && exit 1; }. Ou provavelmente apenas { usage; exit 1; }.
Konsolebox
1
O @konsolebox (uso && exit 1) funciona para ksh, zsh e bash, voltando ao bash 2.0. A sintaxe {...} é recente apenas para 4.0+ do bash. Não me interpretem mal, se uma maneira funcionar bem para você, em seguida, use-a, mas lembre-se de que nem todo mundo usa a mesma implementação do bash que você e devemos codificar de acordo com os padrões posix e não com os bashismos.
Dwight Spencer
Não tenho certeza do que você está dizendo. {...}é uma sintaxe comum e está disponível para a maioria, senão todas as shells baseadas em sh, mesmo as shells mais antigas que não seguem os padrões POSIX.
Konsolebox # 20/16
7

Confira esta planilha de dicas do bash, que pode ajudar muito.

Para verificar a duração dos argumentos passados, use "$#"

Para usar a matriz de argumentos transmitidos, use "$@"

Um exemplo de verificação do comprimento e iteração seria:

myFunc() {
  if [[ "$#" -gt 0 ]]; then
    for arg in "$@"; do
      echo $arg
    done
  fi
}

myFunc "$@"

Isso articulado me ajudou, mas faltavam algumas coisas para mim e minha situação. Espero que isso ajude alguém.

Nick Hall
fonte
0

Caso você queira estar do lado seguro, recomendo usar getopts.

Aqui está um pequeno exemplo:

    while getopts "x:c" opt; do
      case $opt in
        c)
          echo "-$opt was triggered, deploy to ci account" >&2
          DEPLOY_CI_ACCT="true"
          ;;
            x)
              echo "-$opt was triggered, Parameter: $OPTARG" >&2 
              CMD_TO_EXEC=${OPTARG}
              ;;
            \?)
              echo "Invalid option: -$OPTARG" >&2 
              Usage
              exit 1
              ;;
            :)
              echo "Option -$OPTARG requires an argument." >&2 
              Usage
              exit 1
              ;;
          esac
        done

veja mais detalhes aqui, por exemplo, http://wiki.bash-hackers.org/howto/getopts_tutorial

IsaacE
fonte
0

Aqui está um exemplo simples para verificar se apenas um parâmetro é fornecido, caso contrário, saia do script:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
panticz
fonte
-1

Você deve adicionar espaços entre a condição de teste:

if [ $# -ne 1 ]; 
    then echo "illegal number of parameters"
fi

Eu espero que isso ajude.

Fabricio
fonte