Operadores de igualdade de bash (==, -eq)

136

Alguém pode explicar a diferença entre -eqe ==no script bash?

Existe alguma diferença entre os seguintes?

[ $a -eq $b ] e [ $a == $b ]

Simplesmente isso ==é usado apenas quando as variáveis ​​contêm números?

Victor Brunell
fonte

Respostas:

187

É o contrário: =e ==para comparações de strings, -eqé para numéricas. -eqestá na mesma família como -lt, -le, -gt, -ge, e -ne, se isso ajuda a lembrar qual é qual.

==é um bash-ismo, a propósito. É melhor usar o POSIX =. No bash, os dois são equivalentes e, na planície, sh =é o único com garantia de funcionamento.

$ a=foo
$ [ "$a" = foo ]; echo "$?"       # POSIX sh
0
$ [ "$a" == foo ]; echo "$?"      # bash specific
0
$ [ "$a" -eq foo ]; echo "$?"     # wrong
-bash: [: foo: integer expression expected
2

(Observação: cite as expansões variáveis! Não deixe as aspas duplas acima.)

Se você estiver escrevendo um #!/bin/bashroteiro, então eu recomendo usar [[vez . A forma dobrada tem mais recursos, sintaxe mais natural e menos truques que o farão mal. As aspas duplas não são mais necessárias $a, por exemplo:

$ [[ $a == foo ]]; echo "$?"      # bash specific
0

Veja também:

John Kugelman
fonte
1
@DJCrashdummy, [[como um todo, é um ksh-ismo da década de 1980 que o bash (e muitas outras conchas) adotou. Esse é o ponto inteiro - se você tem [[ em tudo , então você pode seguramente assumir que todas as extensões ksh implementado em torno dele ( fnmatch()estilo de correspondência de padrões, ERE expressões regulares com =~, e sim, a supressão da cadeia de divisão e sistema de arquivos englobamento) será acessível. Como a [[sintaxe não é POSIX na sua totalidade, não há perda de portabilidade adicional ao assumir que os recursos com os quais ele nasceu estarão disponíveis.
Charles Duffy
1
@DJCrashdummy, ... o link que você mostrar pontos corretamente que cita às vezes são necessárias no lado direito de [[ $var = $pattern ]], se você quer o que de outra forma seria interpretado como um padrão fnmatch em vez disso ser interpretada como uma string literal. A string foonão tem interpretação não literal, deixando as aspas totalmente seguras; é apenas se o OP quiser corresponder, digamos foo*(com o asterisco como literal, não significando nada que possa vir após a sequência foo), que aspas ou escape serão necessários.
Charles Duffy
@CharlesDuffy infelizmente não tenho idéia do que você está se referindo, porque meu comentário parece ter sido excluído e não me lembro porque já faz um bom tempo. :-(
DJCrashdummy 29/01
@DJCrashdummy, ... hein, eu presumi que você mesmo a retiraria. Basicamente, havia uma objeção a respeito de expansões não citadas como [[sendo um basismo (indicando que esse era o único motivo pelo qual você não estava dando +1 na resposta).
Charles Duffy
@CharlesDuffy não, não é a única razão: além do fato de eu não gostar de bash-isms, ksh-isms etc. para tarefas simples (isso causa apenas problemas e confunde iniciantes), não entendo por que misturar single chaves [ ... ]e duplo sinal de igual ==. : - /
DJCrashdummy 29/01
27

Depende da construção de teste em torno do operador. Suas opções são parênteses duplos, chaves duplas, chaves simples ou teste

Se você usa ((...)) , está testando a equidade aritmética com ==C:

$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1

(Nota: 0significa trueno sentido Unix e diferente de zero é um teste que falhou)

Usar -eqdentro de parênteses duplos é um erro de sintaxe.

Se você estiver usando [...] (ou chaves simples) ou [...] [...] (ou chaves duplas), ou testpoderá usar um de -eq, -ne, -lt, -le, -gt ou -ge como uma comparação aritmética .

$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0

O ==interior de chaves simples ou duplas (ou testcomando) é um dos operadores de comparação de cadeias :

$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1

Como operador de cadeia, =é equivalente ==e observe o espaço em branco ao redor =ou ==é obrigatório.

Enquanto você pode fazer [[ 1 == 1 ]]ou [[ $(( 1+1 )) == 2 ]]está testando a igualdade de cadeias - não a igualdade aritmética.

Portanto, -eqproduz o resultado provavelmente esperado que o valor inteiro de 1+1seja igual a 2, embora o RH seja uma string e tenha um espaço à direita:

$ [[ $(( 1+1 )) -eq  "2 " ]]; echo $?
0

Enquanto uma comparação de cadeias de caracteres pega o espaço à direita e, portanto, a comparação de cadeias falha:

$ [[ $(( 1+1 )) ==  "2 " ]]; echo $?
1

E uma comparação incorreta de cadeias pode produzir a resposta errada completa. '10' é lexicograficamente menor que '2'; portanto, uma comparação de string retorna trueou 0. Muitos são mordidos por esse bug:

$ [[ 10 < 2 ]]; echo $?
0

vs o teste correto para 10 sendo aritmeticamente menor que 2:

$ [[ 10 -lt 2 ]]; echo $?
1

Nos comentários, há uma questão da razão técnica, usando o número inteiro -eqem strings retorna True para strings que não são iguais:

$ [[ "yes" -eq "no" ]]; echo $?
0

O motivo é que o Bash não é digitado . Isso -eqfaz com que as seqüências sejam interpretadas como números inteiros, se possível, incluindo a conversão de base:

$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0

E 0se Bash achar que é apenas uma string:

$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1

Então [[ "yes" -eq "no" ]]é equivalente a[[ 0 -eq 0 ]]

Última observação: muitas extensões específicas do Bash para as Construções de Teste não são POSIX e, portanto, falharão em outros shells. Outras conchas geralmente não suportam [[...]]e ((...))ou== .

dawg
fonte
Estou curioso sobre o motivo técnico para [[ "yes" -eq "no" ]]retornar True. Como o bash coage essas strings a valores inteiros que podem ser comparados? ;-)
odony
4
As variáveis ​​bash são sem tipo e, portanto, [[ "yes" -eq "no" ]]são equivalentes a [[ "yes" -eq 0 ]] ou [[ "yes" -eq "any_noninteger_string" ]]- All True. A -eqcomparação de forças inteiras. O "yes"é interpretado como um número inteiro 0; a comparação é True se o outro número inteiro for um 0ou o resultado da sequência 0.
Dawg
Boo, hiss re: mostrando (não portável) ==nos exemplos de código e apenas mencionando (portátil, padronizado) =embaixo.
Charles Duffy
24

==é um alias específico do bash =e executa uma comparação de sequência (lexical) em vez de uma comparação numérica. eqsendo uma comparação numérica, é claro.

Por fim, geralmente prefiro usar o formulário if [ "$a" == "$b" ]

Elliott Frisch
fonte
15
Usar ==aqui é uma forma incorreta, pois somente =é especificado pelo POSIX.
Charles Duffy
9
Se você realmente insiste em usá ==-lo, coloque-o entre [[e ]]. (E certifique-se de que a primeira linha do seu script especifique o uso /bin/bash).
holgero
18

Caras: Várias respostas mostram exemplos perigosos. O exemplo do OP [ $a == $b ]usou especificamente a substituição de variáveis ​​não citadas (a partir de outubro de 17). Pois [...]isso é seguro para a igualdade de cadeias.

Mas se você enumerar alternativas como [[...]], deve informar também que o lado direito deve ser citado. Se não citado, é uma correspondência de padrão! (Na página de manual do bash: "Qualquer parte do padrão pode ser citada para forçá-lo a corresponder como uma string.").

Aqui no bash, as duas instruções que produzem "yes" correspondem a padrões, outras três são igualdade de cadeias:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$
bk-se
fonte
2
Precisa estar [ "$lht" = "$rht" ] com as aspas para ser confiável, mesmo para a igualdade. Se você tiver criado um arquivo com touch 'Afoo -o AB', [ $lft = $rht ]retornará true, mesmo que esse nome não seja idêntico a AB.
Charles Duffy