/ bin / dash: verifique se $ 1 é um número

12

Qual seria a melhor maneira de verificar se $ 1 é um número inteiro em / bin / dash?

No bash, eu poderia fazer:

[[ $1 =~ ^([0-9]+)$ ]]

Mas isso não parece ser compatível com POSIX e o traço não suporta esse

Martin Vegter
fonte

Respostas:

12

Os seguintes detectam números inteiros, positivos ou negativos, e trabalham sob dashe são POSIX:

Opção 1

echo "$1" | grep -Eq '^[+-]?[0-9]+$' && echo "It's an integer"

opção 2

case "${1#[+-]}" in
    ''|*[!0-9]*)
        echo "Not an integer" ;;
    *)
        echo "Integer" ;;
esac

Ou, com um pouco de uso do :comando (nop):

! case ${1#[+-]} in *[!0-9]*) :;; ?*) ! :;; esac && echo Integer
John1024
fonte
5
E se a sequência contiver novas linhas? foo\n123\nbarnão é um número inteiro, mas passaria nesse teste.
godlygeek
3
A versão grep, é isso. A versão do caso parece correta.
godlygeek
5

Se dash, bash, ksh, zsh, POSIX sh, ou posh( "uma reimplementação do shell Bourne" sh ); o caseconstruto é o mais amplamente disponível e confiável:

case $1 in (*[!0-9]*|"") false ;; (*) true ;; esac
Janis
fonte
Você testou isso dash? Funciona para mim sob, bashmas não dash.
precisa saber é o seguinte
Sim, eu testei no meu sistema também com dash; para interrogar o resultado que adicionei echo $?após o comando case.
Janis
Eu fiz o mesmo (Debian estável), mas sem alegria. Para um exemplo completo que funciona para mim sob meu traço, consulte minha Opção 2 .
precisa saber é o seguinte
Hmm, eu não tenho uma casca de bourne original disponível para testar. Os documentos que eu inspecionei sobre "recursos [no ksh] não no shell bourne" pelo menos não o mencionam, então suponho que ele estivesse lá. Não? - Em seguida, omita o parêntese inicial. - OTOH, posh("uma reimplementação do shell Bourne") também não tem problemas com essa solução.
Janis
1
@mikeserv; Existem várias razões pelas quais eu sempre uso parênteses correspondentes case; um motivo é o bug que você descreve, outro que, nos editores que possuem recursos que dependem de parênteses correspondentes (vim), oferece suporte muito melhor e, não menos importante, acho pessoalmente mais legível tê-los emparelhados. - WRT poshsendo POSIX; bem, a citação da página de manual que eu sugeri sugeriu outra coisa, mas não se pode confiar em tais declarações informais de qualquer maneira, eu acho. De qualquer forma, a velha casca de bourne não é tão significativa agora que estamos na era POSIX.
Janis
1

Você pode usar o -eqteste na string, com ela mesma:

$ dash -c 'a="a"; if [ "$a" -eq "$a" ] ; then echo number; else echo not a number; fi' 
dash: 1: [: Illegal number: a
not a number
$ dash -c 'a="0xa"; if [ "$a" -eq "$a" ] ; then echo number; else echo not a number; fi'
dash: 1: [: Illegal number: 0xa
not a number
$ dash -c 'a="-1"; if [ "$a" -eq "$a" ] ; then echo number; else echo not a number; fi'
number

Se a mensagem de erro for um problema, redirecione a saída de erro para /dev/null:

$ dash -c 'a="0xa"; [ "$a" -eq "$a" ] 2>/dev/null|| echo no'
no
muru
fonte
1
Também diria que " 023 "é um número. Observe que ele funciona com traço, mas nem todos os outros shells POSIX, pois o comportamento não é especificado se os operandos forem números inteiros decimais. Por exemplo, com o ksh, diria isso SHLVLou 1+1é um número.
Stéphane Chazelas
0

Tente usá-lo como uma expansão aritmética e veja se funciona. Na verdade, você precisa ser um pouco mais rigoroso do que isso, porque expansões aritméticas ignorariam os espaços iniciais e finais, por exemplo. Faça uma expansão aritmética e verifique se o resultado expandido corresponde exatamente à variável original.

check_if_number()
{
    if [ "$1" = "$((${1}))" ] 2>/dev/null; then
        echo "Number!"
    else
        echo "not a number"
    fi
}

Isso também aceitaria números negativos - se você realmente quiser excluí-los, adicione uma verificação extra $((${1} >= 0)).

godlygeek
fonte
1
traço não tem[[
glenn jackman
@glennjackman Whoops. Tem $(( ... ))? Nesse caso, minha resposta ainda deve estar materialmente correta, só preciso adicionar algumas citações extras.
godlygeek
é a sua resposta: vá descobrir.
Glenn Jackman
Parece que sim, então minha versão atualizada deve fazer o truque. Eu não tenho traço para testá-lo - então, deixe-me saber se eu perdi mais alguma coisa.
godlygeek
Eu tentei: check_if_number 1.2e a função retornou: dash: 3: arithmetic expression: expecting EOF: "1.2"
John1024 23/04
0

Talvez com expr?

if expr match "$1" '^\([0-9]\+\)$' > /dev/null; then
  echo "integer"
else 
  echo "non-integer"
fi
chave de aço
fonte
nem matchnem \+são POSIX. Também diria que 0 não é um número. Você querexpr "x$1" : 'x[0-9]\{1,\}$'
Stéphane Chazelas
0

No sistema POSIX, você pode usar expr :

$ a=a
$ expr "$a" - 0 >/dev/null 2>&1
$ [ "$?" -lt 2 ] && echo Integer || echo Not Integer
cuonglm
fonte
Ele dirá que 0 não é um número inteiro (e dizer -12 é o que a solução do bash do OP teria rejeitado). Algumas exprimplementações dirão que 9999999999999999999 não é um número inteiro. O POSIX não oferece garantia de que isso funcionará. Na prática, pelo menos em um sistema GNU, ele dirá que "length" é um número inteiro.
Stéphane Chazelas
No meu teste, ele funciona pelo menos no Debian e OSX. Diz inteiro para 0. Posix garante que $ a deve ser uma expressão válida (inteiro) para aritmética expr.
cuonglm
Oh sim, desculpe, eu tinha esquecido seu teste por US $? <2. Ainda expr 9999999999999999999 + 0me dá um status 3 saída e expr -12 + 0e expr length + 0me dar um status de saída 0 com GNU expr ( + stringforças stringa serem consideradas como uma string com o GNU expr. expr "$a" - 0Funcionaria melhor).
Stéphane Chazelas
@ StéphaneChazelas: Ah, sim, atualizei minha resposta. Eu acho que -12é um número inteiro válido, e 9999999999999999999deu um estouro.
cuonglm
0

Aqui está uma função simples usando o mesmo método da resposta do muru :

IsInteger()      # usage: IsInteger string
{               #  returns: flag
        [ "$1" -eq "$1" ] 2> /dev/null
}

Exemplo:

p= n=2a3; IsInteger $n || p="n't" ; printf "'%s' is%s an integer\n" "$n" "$p"
p= n=23;  IsInteger $n || p="n't" ; printf "'%s' is%s an integer\n" "$n" "$p"

Resultado:

'2a3' isn't an integer
'23' is an integer
agc
fonte