Existe alguma diferença importante ao comparar uma variável como uma string ou como um int

22

Por curiosidade, ao fazer uma comparação de variáveis ​​bash (seu valor é um integer), é possível testá-lo em relação a algum valor predefinido, declarado como intou como string.

Exemplo de script :

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Saída :

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

e

$  ./param.sh blah
$

Ambas as funções se comportam da mesma maneira, e então eu estou querendo saber se há uma maneira preferida ao verificar uma variável inteira? Eu iria para a verificação intversus int, pois é mais rigoroso, mas gostaria de saber se há alguma desvantagem em fazê-lo string?

Nesse caso, f2()também é mais rigoroso quanto à comparação, ou seja, passar um valor decimal o quebrará, enquanto f1()isso não causará problemas.

fduff
fonte
Observe que o Bash realmente não tem um tipo de dados Inteiro. Basicamente, você pode apenas sugerir que o Bash trate uma String como um número inteiro.
Helpermethod

Respostas:

18

Sim, muitas diferenças. Por exemplo, =verifica a igualdade exata da string, mas -eqavalia as duas expressões aritmeticamente antes de verificar a igualdade:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Além disso, a cadeia vazia é numericamente igual a zero:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

E toda uma outra classe de diferenças aparece quando você coloca os operadores de comparação - considerando <vs -lt, por exemplo:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

Isso ocorre porque a sequência "2" é alfabética após a sequência "10" (já que 1 vem antes de 2), mas o número "2" é numericamente menor que o número "10".

godlygeek
fonte
2
Não esqueça que também (( ... ))há operações numéricas. (( " 1 " == 1 )) && echo yes || echo noresultados emyes
Patrick
7

A comparação entre números inteiros e seqüências de caracteres se torna mais significativa quando você está comparando maior ou menor que:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

O primeiro falha porque 9 vem após 11 quando classificado lexicograficamente.

Observe que o uso de aspas não determina se você está comparando cadeias ou números, o operador faz. Você pode adicionar ou remover as aspas acima, isso não faz nenhuma diferença. O Bash captura variáveis ​​indefinidas entre colchetes duplos, portanto, as aspas não são necessárias. Usar aspas com colchetes simples para testes numéricos não o salvará, pois:

[ "" -lt 11 ]

de qualquer maneira, é um erro ("é necessária uma expressão inteira"). As cotações são uma salvaguarda eficaz com comparações de cadeias entre colchetes:

[ "" \< 11 ]

Observe entre parênteses duplos , mas ""será que -eq 0não == 0.

Cachinhos Dourados
fonte
1
No bash, não é estritamente necessário citar variáveis ​​entre colchetes: o builtin [[é inteligente o suficiente para lembrar onde estão as variáveis ​​e não será enganado por variáveis ​​vazias. Parênteses simples ( [) não possuem esse recurso e requerem aspas.
Glenn Jackman
@glennjackman Não tinha notado isso. [[ -lt 11 ]]é um erro, mas nothing=; [[ $nothing -lt 11 ]]não é. Eu refiz um pouco o último parágrafo.
10289 goldilocks
2

Além do que foi dito.
A comparação da igualdade é mais rápida com os números, embora nos scripts de shell seja raro que você precise de um cálculo rápido.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s
Emmanuel
fonte
Considerando que eles fazem coisas diferentes, eu diria que o desempenho é irrelevante - você precisa usar o que faz o que deseja.
10118 godlygeek
@godlygeek A comparação de igualdade de uma variável pode ser obtida nos dois sentidos. "-eq" é mais rápido.
Emmanuel
Eles testam diferentes definições de igualdade. Se você deseja responder à pergunta "Essa variável contém a string exata 123", você pode usar apenas =, pois o uso -eqcorresponderia a "+123" também. Se você quiser saber "Essa variável, quando avaliada como uma expressão aritmética, compara com igual a 123", você só pode usar -eq. A única vez em que vejo onde um programador não se importaria com qual definição de igualdade foi usada é quando ele sabe que o conteúdo da variável é restrito a um padrão específico antes do tempo.
godlygeek
@godlygeek interessante, a questão era comparar a igualdade de números como se fossem strings, isso se encaixa no caso de variáveis ​​restritas antecipadamente a um padrão específico?
Emmanuel
Seu exemplo ( b=234) se encaixa nesse padrão - você sabe que não é +234 ou "234" ou "233 + 1", desde que você o designou, então você sabe que compará-lo como uma sequência e como um número são igualmente válidos. Mas o roteiro do OP, uma vez que leva de entrada como um argumento de linha de comando, não tem essa restrição - considerar chamar-lo como ./param.sh 0+1ou./param.sh " 1"
godlygeek