Verifique a existência do argumento de entrada em um script de shell Bash

1339

Eu preciso verificar a existência de um argumento de entrada. Eu tenho o seguinte script

if [ "$1" -gt "-1" ]
  then echo hi
fi

eu recebo

[: : integer expression expected

Como verifico o argumento de entrada1 primeiro para ver se ele existe?

user775187
fonte

Respostas:

2331

Isto é:

if [ $# -eq 0 ]
  then
    echo "No arguments supplied"
fi

A $#variável informará o número de argumentos de entrada pelos quais o script foi passado.

Ou você pode verificar se um argumento é uma string vazia ou não é como:

if [ -z "$1" ]
  then
    echo "No argument supplied"
fi

A -zopção testará se a expansão de "$ 1" é uma sequência nula ou não. Se for uma cadeia nula, o corpo será executado.

phoxis
fonte
62
Eu gosto de fazê-lo desta maneira, em sintaxe concisa e ainda POSIX aceitável. [ -z "$1" ] && echo "No argument supplied" Eu prefiro one-liners, porque são mais fáceis para mim; e é também mais rápido para o valor controlo de saída, em comparação ao usoif
JM Becker
168
Você provavelmente deseja adicionar um exit 1no final de seus ecos dentro do bloco if quando o argumento é necessário para o script funcionar. Óbvio, mas vale a pena notar pela perfeição.
msanford
16
É possível, embora raramente útil, que o primeiro argumento seja inicializado, mas vazio; programname "" secondarg third. A $#verificação verifica inequivocamente o número de argumentos.
Tripleee
39
Para um noob, especialmente alguém que tem experiência em não scripts, também é importante mencionar algumas peculiaridades sobre essas coisas. Você também pode ter mencionado que precisamos de um espaço após a abertura e a chave de fechamento. Caso contrário, as coisas não funcionam. Eu sou um noob de script (eu venho do fundo C) e achei da maneira mais difícil. Foi só quando eu decidi copiar a coisa toda "como está" que as coisas funcionaram para mim. Foi então que percebi que tinha que deixar um espaço depois da chave de abertura e antes da chave de fechamento.
HighOnMeat 13/09/13
72
e para args opcionaisif [ ! -z "$1" ]; then ...
gcb 26/02/14
347

É melhor demonstrar dessa maneira

if [[ $# -eq 0 ]] ; then
    echo 'some message'
    exit 1
fi

Você normalmente precisa sair se tiver muito poucos argumentos.

Val
fonte
72
Não, não é: isso tem o exit 1que você normalmente deseja e usa o [[ ]]teste que (iirc) geralmente é mais razoável. Portanto, para as pessoas que copiam e colam cegamente o código, essa é a melhor resposta.
dshepherd
42
Para saber mais sobre a diferença entre [] e [[]], consulte stackoverflow.com/questions/3427872/…
Sebastián Grignoli
104

Em alguns casos, é necessário verificar se o usuário transmitiu um argumento para o script e, se não, retornar ao valor padrão. Como no script abaixo:

scale=${2:-1}
emulator @$1 -scale $scale

Aqui, se o usuário não passou scale segundo parâmetro, inicio o emulador do Android -scale 1por padrão. ${varname:-word}é um operador de expansão. Existem outros operadores de expansão também:

  • ${varname:=word} qual define o indefinido em varnamevez de retornar o wordvalor;
  • ${varname:?message}que retorna varnamese está definido e não é nulo ou imprime messagee anula o script (como no primeiro exemplo);
  • ${varname:+word}que retorna wordapenas se varnamefor definido e não for nulo; retorna nulo caso contrário.
Aleks N.
fonte
1
O exemplo acima parece usar ${varname?message}. O extra é :um erro de digitação ou muda de comportamento?
Eki
6
Eki, o ":" é um comando interno e uma abreviação de / bin / true neste exemplo. Ele representa um comando do-nothing que basicamente ignora os argumentos fornecidos. É essencial neste teste para impedir que o intérprete tente executar o conteúdo de "$ varname" (o que você certamente NÃO deseja que aconteça). Também vale a pena notar; você pode testar quantas variáveis ​​com este método desejar. E tudo com mensagens de erro específicas. ie: ${1?"First argument is null"} ${2?"Please provide more than 1 argument"}
user.friendly
48

Tentar:

 #!/bin/bash
 if [ "$#" -eq  "0" ]
   then
     echo "No arguments supplied"
 else
     echo "Hello world"
 fi
Ranjithkumar T
fonte
4
Por que você precisa de aspas duplas para $#e 0?
User13107
1
Não tem problema se usarmos sem aspas duplas, como $ # e 0 #
Ranjithkumar T
nas janelas, mingw este é o único caminho a percorrer.
Lajos Meszaros
2
Esta resposta fornece um excelente ponto de partida para um script que acabei de criar. Obrigado por mostrar o elsetambém.
31720 Chris K
2
@ user13107 variáveis ​​com aspas duplas no bash evitam globbing (como expandir nomes de arquivos como foo*) e dividir palavras (ou seja, dividir o conteúdo se o valor contiver espaço em branco). Nesse caso, não é necessário citar $#porque esses dois casos não se aplicam. Citar o 0também não é necessário, mas algumas pessoas preferem citar valores, pois são realmente strings e isso torna mais explícito.
Dennis
39

Outra maneira de detectar se os argumentos foram passados ​​para o script:

((!$#)) && echo No arguments supplied!

Observe que (( expr ))faz com que a expressão seja avaliada de acordo com as regras da Aritmética do Shell .

Para sair na ausência de argumentos, pode-se dizer:

((!$#)) && echo No arguments supplied! && exit 1

Outra maneira (análoga) de dizer o acima seria:

let $# || echo No arguments supplied

let $# || { echo No arguments supplied; exit 1; }  # Exit if no arguments!

help let diz:

let: let arg [arg ...]

  Evaluate arithmetic expressions.

  ...

  Exit Status:
  If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.
devnull
fonte
2
-1, esse pode ser o pior método para validar a existência de um argumento. Além disso, pode desencadear a substituição do histórico e, potencialmente, fazer coisas ruins.
user.friendly
2
em vez de exitque mata o meu processo zsh, eu uso returno que não matá-lo
Timo
Por que ((!$#))acionaria a substituição do histórico?
Zhro 25/09/19
25

Costumo usar esse trecho para scripts simples:

#!/bin/bash

if [ -z "$1" ]; then
    echo -e "\nPlease call '$0 <argument>' to run this command!\n"
    exit 1
fi
f2cx
fonte
1
Então, isso deve ser usado em você precisa de apenas um argumento?
Danijel
21

Somente porque há um ponto mais básico a ser destacado, acrescentarei que você pode simplesmente testar se sua string é nula:

if [ "$1" ]; then
  echo yes
else
  echo no
fi

Da mesma forma, se você está esperando arg count, basta testar o seu último:

if [ "$3" ]; then
  echo has args correct or not
else
  echo fixme
fi

e assim por diante com qualquer arg ou var

seorphates
fonte
4

Se você deseja verificar se o argumento existe, verifique se o número de argumentos é maior ou igual ao número do argumento de destino.

O script a seguir demonstra como isso funciona

test.sh

#!/usr/bin/env bash

if [ $# -ge 3 ]
then
  echo script has at least 3 arguments
fi

produz a seguinte saída

$ ./test.sh
~
$ ./test.sh 1
~
$ ./test.sh 1 2
~
$ ./test.sh 1 2 3
script has at least 3 arguments
$ ./test.sh 1 2 3 4
script has at least 3 arguments
Brad Parks
fonte
3

Como um pequeno lembrete, os operadores de teste numéricos em Bash só funcionam em números inteiros ( -eq, -lt, -ge, etc.)

Eu gosto de garantir que meus $ vars sejam ints

var=$(( var + 0 ))

antes de testá-los, apenas para me defender do erro "[: é necessário um número inteiro".

Cwissy
fonte
1
Bom truque, mas observe: devido à incapacidade do bash de lidar com flutuadores na aritmética, esse método pode causar um erro de sintaxe e retornar um valor diferente de zero, o que seria um obstáculo para o errexit. var=$(printf "%.0f" "$var")pode lidar com flutuadores, mas sofre com a saída diferente de zero quando recebe uma string. Se você não se importa um awk, este método uso I parece ser o mais robusto para impor um inteiro: var=$(<<<"$var" awk '{printf "%.0f", $0}'). Se var estiver desativado, o padrão será "0". Se var for flutuante, será arredondado para o número inteiro mais próximo. Valores negativos também são bons de usar.
user.friendly
0

validação de função bash de um liner

myFunction() {

    : ${1?"forgot to supply an argument"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

adicionar nome e uso da função

myFunction() {

    : ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

adicione validação para verificar se inteiro

para adicionar validação adicional, por exemplo, para verificar se o argumento passado é um número inteiro, modifique a validação de um forro para chamar uma função de validação:

: ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"} && validateIntegers $1 || die "Must supply an integer!"

em seguida, construa uma função de validação que valide o argumento, retornando 0 em caso de sucesso, 1 em caso de falha e uma função de dado que anula o script em caso de falha

validateIntegers() {

    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
        return 1 # failure
    fi
    return 0 #success

}

die() { echo "$*" 1>&2 ; exit 1; }

Ainda mais simples - basta usar set -u

set -u certifica-se de que todas as variáveis ​​referenciadas sejam definidas quando usadas, basta configurá-las e esquecê-las

myFunction() {
    set -u
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}
AndrewD
fonte