Comparando números inteiros: expressão aritmética ou expressão condicional

20

No Bash, dois números inteiros podem ser comparados usando expressão condicional

arg1 OP arg2

OP é um dos -eq, -ne, -lt, -le, -gt, ou -ge. Esses operadores binários aritméticos retornam true se arg1 for igual a, diferente de, menor que, menor que ou igual a, maior que ou maior que ou igual a arg2 , respectivamente. Arg1 e arg2 podem ser inteiros positivos ou negativos.

ou expressão aritmética:

<= >= < > comparação

== != igualdade e desigualdade

Por que temos duas maneiras diferentes de comparar dois números inteiros? Quando usar qual?

Por exemplo, [[ 3 -lt 2 ]]usa expressão condicional e (( 3 < 2 ))expressão aritmética. Ambos retornam 0 quando a comparação é verdadeira

Ao comparar dois números inteiros, esses dois métodos sempre podem ser usados ​​de forma intercambiável? Se sim, por que o Bash tem dois métodos em vez de um?

Tim
fonte
11
= != < <= > >=comparar cadeias . 1 -eq 01mas 1 != 01e 8 -lt 42mas8 > 42
dave_thompson_085
Eles estão sobrecarregados em expressões aritméticas.
Tim
11
você precisará pesquisar nos registros de alterações do bash para descobrir quando cada recurso foi adicionado. Eu suspeito que as expressões aritméticas foram adicionadas muito mais tarde que o comando test.
Glenn Jackman
Não estou perguntando sobre a comparação de strings. @muru.
Tim

Respostas:

28

Sim, temos duas maneiras diferentes de comparar dois números inteiros.

Parece que esses fatos não são amplamente aceitos neste fórum:

  1. Dentro da linguagem [ ]dos operadores de comparação aritmética são -eq, -ne, -lt, -le, -gte -ge.

    Como eles também estão dentro de um comando de teste e dentro de um [[ ]].

    Sim dentro deste expressões idiomáticas, =, <, etc, são operadores de string.

  2. Dentro da linguagem (( ))dos operadores de comparação aritmética são ==, !=, <, <=, >, e >=.

    Não, essa não é uma "expansão aritmética" (que começa com a $) como $(( )). É definido como um "Comando composto" no man bash.

    Sim, segue as mesmas regras (internamente) da "expansão aritmética", mas não tem saída, apenas um valor de saída. Pode ser usado assim:

if (( 2 > 1 )); then ...

Por que temos duas maneiras diferentes de comparar dois números inteiros?

Eu acho que o último (( ))foi desenvolvido como uma maneira mais simples de realizar testes aritméticos. É quase o mesmo que o, $(( ))mas simplesmente não tem saída.

Por que dois? Bem, a mesma que por isso que temos dois printf(externa e builtin) ou quatro teste (externo test, builtin test, [e [[). É assim que as conchas crescem, melhorando alguma área em um ano, melhorando outras no próximo ano.

Quando usar qual?

Essa é uma pergunta muito difícil, porque não deve haver diferença efetiva. Claro que existem algumas diferenças na maneira como um [ ]trabalho e um (( ))trabalho internamente, mas: qual é o melhor para comparar dois números inteiros? Qualquer um!.

Ao comparar dois números inteiros, esses dois métodos sempre podem ser usados ​​de forma intercambiável?

Por dois números, sou obrigado a dizer que sim.
Mas para variáveis, expansões, operações matemáticas, pode haver diferenças importantes que devem favorecer uma ou outra. Não posso dizer que absolutamente ambos sejam iguais. Por um lado, o (( ))poderia executar várias operações matemáticas em sequência:

if (( a=1, b=2, c=a+b*b )); then echo "$c"; fi

Se sim, por que o Bash tem dois métodos em vez de um?

Se ambos são úteis, por que não?

Yurij Goncharuk
fonte
11
=é uma tarefa e ==é uma comparação em expansões aritméticas. A pergunta cita corretamente. Mas a resposta está errada.
ceving 16/08/19
12

Historicamente, o testcomando existia primeiro (pelo menos desde a Unix Seventh Edition em 1979). Usou-se os operadores =e !=para comparar cadeias, e -eq, -ne, -lt, etc, para comparar os números. Por exemplo, test 0 = 00é falso, mas test 0 -eq 00é verdadeiro. Não sei por que essa sintaxe foi escolhida, mas pode ter sido para evitar o uso <e >, que o shell teria analisado como operadores de redirecionamento. O testcomando obteve outra sintaxe alguns anos depois: [ … ]é equivalente a test ….

A [[ … ]]sintaxe condicional, dentro da qual <e >pode ser usada como operadores sem citar, foi adicionada posteriormente, no ksh. Ele manteve a compatibilidade com versões anteriores e [ … ], portanto, usou os mesmos operadores, mas adicionou <e >comparou seqüências de caracteres (por exemplo, [[ 9 > 10 ]]mas [[ 9 -lt 10 ]]). Para obter mais informações, consulte Uso de colchetes simples ou duplos - bash

As expressões aritméticas também vieram depois do testcomando, no shell Korn , em algum momento da década de 1980. Eles seguiram a sintaxe da linguagem C, que era muito popular nos círculos Unix. Assim, eles usaram operadores de C: ==para igualdade, <=para menor ou igual, etc.

O Unix Seventh Edition não tinha expressões aritméticas, mas possuía o exprcomando , que também implementava uma sintaxe do tipo C para operações inteiras, incluindo seus operadores de comparação. Em um script de shell, os caracteres <e >tiveram que ser citados para protegê-los do shell, por exemplo, if expr 1 \< 2; …é equivalente a if test 1 -lt 2; …. A adição de expressões aritméticas ao shell fez com que a maioria dos usos exprfosse obsoleta, por isso não é bem conhecido hoje.

Em um script sh, você geralmente usaria expressões aritméticas para calcular um valor inteiro e [ … ]comparar números inteiros.

if [ "$((x + y))" -lt "$z" ]; then 

Em um script ksh, bash ou zsh, você pode usar ((…))para ambos.

if ((x + y < z)); then 

O [[ … ]]formulário é útil se você deseja usar condicionais envolvendo outras coisas além de números inteiros.

Gilles 'SO- parar de ser mau'
fonte
1

De acordo com a página do manual de teste, = e! = São usados ​​para comparações de cadeias, enquanto as expressões -eq, -gt, -lt, -ge, -le e -ne são comparações inteiras. Eu sempre segui essa convenção ao escrever scripts de shell e sempre funciona. Esteja ciente de que se você tiver variáveis ​​na expressão, pode ser necessário citar as variáveis ​​de alguma forma para evitar uma comparação nula.

No papel, fazemos comparações de string / número sem muita reflexão. Um computador, por outro lado, não sabe se 987 é um número ou uma sequência de caracteres. Você precisa dos diferentes operadores para informar ao computador o que fazer para obter o resultado certo. Há algumas informações adicionais aqui que explicam parte da história. Essencialmente, as variáveis ​​não são tipadas e permaneceram assim para compatibilidade histórica.

signal7
fonte
No meu post, = e !=são operadores aritméticos, enquanto a página de manual testmostra apenas operadores de expressão condicional.
Tim