verificar um argumento para um script bash é uma cadeia de todos os dígitos

4

A FAQ do Bash diz

Se você está validando uma simples "string de dígitos", você pode fazer isso com um glob:

# Bash
if [[ $foo = *[!0-9]* ]]; then
    echo "'$foo' has a non-digit somewhere in it"
else
    echo "'$foo' is strictly numeric"
fi

Pensei: "Bom, isso parece legal e simples". Em seguida, colei exatamente isso em um script, exceto que adicionei "exit 1" após o primeiro eco e alterei $foo para $1 , de modo que parecesse

if [[ $1 = *[!0-9]* ]]; then
    echo "'$1' has a non-digit somewhere in it"
    exit 1
else
    echo "'$1' is strictly numeric"
fi

Eu então tentei rodar isso e peguei

$ sh foo.sh bar
bar
foo.sh: 6: [[: not found
'bar' is strictly numeric

Sou Bash analfabeto, tenho vergonha de dizer, então não tenho ideia do que poderia estar errado aqui. Tive a impressão, apoiada pelo manual de Bash on-line , de que o operador para correspondência com regexes é =~ , mas mudando isso não faz diferença. E que o operador [[ que parece ser problemático aqui parece padrão, embora eu não saiba qual é a diferença entre [[ ]] e [ ] , que correspondem ao teste da expressão, tanto quanto eu sei. Estou usando o Debian squeeze com o bash

$ bash --version
GNU bash, version 4.1.5(1)-release (i486-pc-linux-gnu)

O Debian diz a versão 4.1-3 .

    
por Faheem Mitha 28.04.2011 / 23:23

3 respostas

9

Por que você está chamando sh , se é um script bash? É claro que no seu sistema, sh não é bash, mas algum outro shell na família Bourne / POSIX. Na verdade, é dash , um shell menor projetado para baixo consumo de memória e velocidade que praticamente só suporta < a href="http://pubs.opengroup.org/onlinepubs/009695399/idx/xcu.html"> POSIX constrói e utilitários integrados.

[[ … ]] é uma extensão ksh para a sintaxe Bourne que foi escolhida por bash e zsh mas não por POSIX. Em um script portátil, você precisa usar [ … ] para testes. A construção padrão não tem nenhum suporte para correspondência de padrões; o idioma padrão é usar um case construct :

case $1 in                        # branch to the first pattern that $1 matches
  *[!0-9]*)                       # pattern = anything containing a non-digit
    echo not a number             # do this if the first pattern triggered
    ;;                            # end of this case branch
  *)                              # pattern = anything (else)
    echo successor of $(($1-1))   # do this if the second pattern triggered
    ;;                            # end of this case branch
esac                              # end of the case construct

Aqui está uma função que testa se o argumento é de todos os dígitos:

is_all_digits () {
  case $1 in *[!0-9]*) false;; esac
}

Digression : Eu inicialmente cometi um erro de digitação no snippet acima: Escrevi $(($0-1)) . Isso causou mensagens de erro com aparência estranha:

$ ash foo.sh 42
foo.sh: 4: arithmetic expression: expecting EOF: "foo.sh-1"
$ ash ./foo.sh 42
./foo.sh: 4: arithmetic expression: expecting primary: "./foo.sh-1"
$ ksh ./foo.sh 42
./foo.sh: line 3: foo.sh: invalid variable name
$ pdksh ./foo.sh 42
./foo.sh[4]: ./foo.sh-1: unexpected '.'
$ bash foo.sh 42         
foo.sh: line 3: foo.sh-1: syntax error: invalid arithmetic operator (error token is ".sh-1")
$ bash ./foo.sh 42
./foo.sh: line 3: ./foo.sh-1: syntax error: operand expected (error token is "./foo.sh-1")
$ zsh foo.sh 42
foo.sh:3: bad floating point constant

$0 é o nome do script, portanto, a expressão aritmética a ser avaliada é foo.sh-1 ou ./foo.sh-1 . Você pode observar a diversidade de mensagens de erro entre os shells. Fiquei um pouco surpreso ao ver que as mensagens das cinzas e a mensagem de bash sem ./ eram as mais claras: nenhuma das outras conchas menciona que o problema está em uma expressão aritmética. Ash e pdksh conseguem pontos atracados por reportar o erro em uma linha longe demais.

    
por 28.04.2011 / 23:36
3

Tente executá-lo com bash foo.sh bar . Se você vai escrever um script bash, você precisa usar o bash. Os [[e]] usados acima são apenas em bash que é um derivado shell Bourne. sh significa Bourne SHell e pode não ser o mesmo que bash. Eu acho que debian usa dash para sh. Se, em vez disso, você quiser aprender a escrever scripts de shell Bourne portáveis que não exigem recursos específicos do bash, é possível reescrever isso usando grep:

if echo "$foo" | grep '^[0-9][0-9]*$' >/dev/null 2>&1; then
    echo "'$1' has a non-digit somewhere in it"
    exit 1
else
    echo "'$1' is strictly numeric"
fi
    
por 28.04.2011 / 23:33
0

Sugiro usar expr porque é compatível com POSIX:

if [ "$(expr "$1" : '^[0-9]\+$')" = "0" ]; then
    echo "'$1' has a non-digit somewhere in it"
    exit 1
else
     echo "'$1' is strictly numeric"
fi
    
por 29.04.2011 / 07:03