Comparando inteiros: expressão aritmética ou expressão condicional

10

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

arg1 OP arg2

OP is one of -eq, -ne, -lt, -le, -gt, or -ge. These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to arg2, respectively. Arg1 and arg2 may be positive or negative integers.

ou expressão aritmética:

<= >= < > comparison

== != equality and inequality

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

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

Ao comparar dois inteiros, esses dois métodos podem sempre ser usados de maneira intercambiável? Se sim, porque o Bash tem dois métodos em vez de um?

    
por Tim 24.04.2016 / 12:21

3 respostas

18

Sim, temos duas maneiras diferentes de comparar dois inteiros.

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

  1. Dentro do idioma [ ] , os operadores para comparação aritmética são -eq , -ne , -lt , -le , -gt e -ge .

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

    Sim, dentro desta expressão, = , < , etc. são operadores de string.

  2. Dentro do idioma (( )) , os operadores para comparação aritmética são = , != , < , <= , > e >= .

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

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

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

Por que temos duas maneiras diferentes de comparar dois inteiros?

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

Por que dois? Bem, da mesma forma que temos dois printf (externos e internos) ou quatro testes (externos test , internos test , [ e [[ ). É assim que as cascas crescem, melhorando algumas áreas em um ano, melhorando outras no ano seguinte.

Quando usar qual?

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

Ao comparar dois inteiros, esses dois métodos podem sempre ser usados de maneira intercambiável?

Por dois números, sou obrigado a dizer sim.
Mas, para variáveis, expansões, operações matemáticas, pode haver diferenças fundamentais que devem favorecer uma ou outra. Eu não posso dizer que absolutamente ambos são 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 forem úteis, por que não?

    
por 25.04.2016 / 00:08
9

Historicamente, o comando test existia primeiro (pelo menos até Sétima Edição do Unix em 1979). Usou os operadores = e != para comparar strings e -eq , -ne , -lt , etc. para comparar 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 de < e > , que o shell teria analisado como operadores de redirecionamento. O comando test obteve outra sintaxe alguns anos depois: [ … ] é equivalente a test … .

A sintaxe condicional [[ … ]] , dentro da qual < e > podem ser usados como operadores sem cotação, foi adicionada posteriormente, em ksh. Ele manteve a compatibilidade com [ … ] , então usou os mesmos operadores, mas adicionou < e > para comparar strings (por exemplo, [[ 9 > 10 ]] but [[ 9 -lt 10 ]] ). Para obter mais informações, consulte usando um ou dois colchetes - bash

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

A Sétima Edição do Unix não tinha expressões aritméticas, mas tinha o expr command , que também implementou uma sintaxe semelhante a C para operações com números inteiros, incluindo seus operadores de comparação. Em um script de shell, os caracteres < e > tinham 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 tornou a maioria dos usos de expr obsoletos, por isso não é bem conhecido hoje em dia.

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

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

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

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

A forma [[ … ]] é útil se você quiser usar condicionais envolvendo coisas diferentes de inteiros.

    
por 24.04.2016 / 23:14
0

De acordo com a página man do 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, talvez seja necessário citar as variáveis de alguma forma para evitar fazer 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 que os diferentes operadores digam ao computador o que fazer para que você obtenha o resultado correto. Há algumas informações adicionais aqui que explicam parte do histórico. Essencialmente, as variáveis não são tipificadas e permaneceram assim para compatibilidade histórica.

    
por 24.04.2016 / 16:42