Операторы равенства Баша (==, -eq)

136

Может кто-нибудь объяснить разницу между сценариями bash -eqи ==в них?

Есть ли разница между следующим?

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

Это просто ==используется только тогда, когда переменные содержат числа?

Виктор Брунелл
источник

Ответы:

187

Это наоборот: =и ==для сравнения строк, -eqи для числовых. -eqнаходится в той же семье , как -lt, -le, -gt, -geи -ne, если это поможет вам вспомнить , что есть что.

==это, кстати, башизм. Лучше использовать POSIX =. В bash они эквивалентны, а в обычном sh =- единственный гарантированно работающий.

$ 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

(Примечание: цитируйте эти расширения переменных! Не оставляйте двойные кавычки выше.)

Если вы пишете #!/bin/bashсценарий, я рекомендую использовать [[вместо него . Удвоенная форма имеет больше возможностей, более естественный синтаксис и меньше ошибок, которые могут вас сбить с толку. Двойные кавычки больше не нужны $a, например:

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

Смотрите также:

Джон Кугельман
источник
1
@DJCrashdummy, [[в целом, представляет собой кш-изм 1980-х годов, принятый bash (и многими другими оболочками). Это все дело - если у вас есть [[ вообще , то можно смело предположить , что все расширения КШ реализованы вокруг него ( fnmatch()-стиль сопоставление с образцом, ERE регулярные выражения с =~, и да, подавление строки расщепления и файловой системы подстановка) будет доступный. Поскольку [[это не POSIX синтаксис в целом, нет дополнительной потери переносимости, если предположить, что функции, с которыми он был создан, будут доступны.
Чарльз Даффи
1
@DJCrashdummy ... ссылка вы показываете точки правильно , что кавычки иногда необходимы на правой стороне от [[ $var = $pattern ]], если вы хотите , что бы в противном случае можно интерпретировать как шаблон fnmatch вместо этого следует интерпретировать как символьную строку. Строка fooне имеет нелитеральной интерпретации, что делает исключение кавычек полностью безопасным; только если OP хочет сопоставить, скажем, foo*(со звездочкой как буквальной, не означающей ничего, что может идти после строки foo), нужны кавычки или экранирование.
Чарльз Даффи
@CharlesDuffy, к сожалению, я понятия не имею, о чем вы имеете в виду, потому что мой комментарий, кажется, удален, и я не могу вспомнить, потому что это было давно. :-(
DJCrashdummy
@DJCrashdummy, ... да, я предполагал, что ты сам забрал его. По сути, это было возражение о том, что внутренние расширения [[без кавычек являются башизмом (указывающим на то, что это была единственная причина, по которой вы не дали ответ +1).
Чарльз Даффи
@CharlesDuffy нет, это не единственная причина: помимо того, что я не люблю баш-измы, кш-измы и т. Д. Для простых задач (это просто вызывает проблемы и сбивает с толку новичков), я не понимаю, зачем смешивать сингл фигурные скобки [ ... ]и двойной знак равенства ==. : - /
DJCrashdummy
27

Это зависит от тестовой конструкции вокруг оператора. Ваши варианты: двойные скобки, двойные фигурные скобки, одинарные фигурные скобки или тест

Если вы используете ((...)) , вы проверяете арифметическое равенство, ==как в C:

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

(Примечание: 0означает trueв смысле Unix, а ненулевое значение - неудачный тест)

Использование -eqвнутри двойных скобок является синтаксической ошибкой.

Если вы используете [...] (или одинарную скобку) или [[...]] (или двойную скобку), или testвы можете использовать один из -eq, -ne, -lt, -le, -gt или -ge как арифметическое сравнение .

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

==Внутри одинарные или двойные скобки (или testкоманды) является одним из операторов сравнения строк :

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

Как строковый оператор, =эквивалентен ==и обратите внимание на пробелы вокруг =или ==его обязательные.

Хотя вы можете это сделать, [[ 1 == 1 ]]или [[ $(( 1+1 )) == 2 ]]он проверяет равенство строк, а не арифметическое равенство.

Таким образом, -eqполучается результат, который, вероятно, ожидался, что целочисленное значение 1+1равно, 2даже если RH является строкой и имеет конечный пробел:

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

В то время как сравнение строк одного и того же занимает конечное пространство, и поэтому сравнение строк не выполняется:

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

А ошибочное сравнение строк может дать совершенно неправильный ответ. «10» лексикографически меньше «2», поэтому при сравнении строк возвращается trueили 0. Так многих укусила эта ошибка:

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

против правильного теста для 10, арифметически меньше 2:

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

В комментариях возникает вопрос о технической причине, по которой использование целого числа -eqв строках возвращает True для строк, которые не совпадают:

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

Причина в том, что Bash не типизирован . По -eqэтой причине строки интерпретируются как целые числа, если это возможно, включая базовое преобразование:

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

И 0если Bash думает, что это просто строка:

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

Так [[ "yes" -eq "no" ]]эквивалентно[[ 0 -eq 0 ]]

Последнее примечание: многие из специфических для Bash расширений тестовых конструкций не являются POSIX и поэтому не работают в других оболочках. Другие оболочки обычно не поддерживают [[...]]и ((...))или== .

Dawg
источник
Мне любопытно , о технической причине для [[ "yes" -eq "no" ]]возвращения True. Как bash переводит эти строки в целые числа, которые можно сравнивать? ;-)
odony
4
Переменные Bash не типизированы, поэтому [[ "yes" -eq "no" ]]эквивалентны [[ "yes" -eq 0 ]] or [[ "yes" -eq "any_noninteger_string" ]]- All True. Эти -eqсилы целого сравнения. "yes"Интерпретируется как целое число 0; сравнение истинно, если другое целое число равно либо 0или результат строки равен 0.
dawg
Бу, шипение re: показ (непереносимый) ==в примерах кода и только упоминание (переносимый, стандартизованный) =внизу.
Чарльз Даффи
24

==является псевдонимом, специфичным для bash, =и выполняет строковое (лексическое) сравнение вместо числового. eqэто, конечно, числовое сравнение.

Наконец, я обычно предпочитаю использовать форму if [ "$a" == "$b" ]

Эллиот Фриш
источник
15
Использование ==здесь является плохим тоном, так как =это указано только в POSIX.
Чарльз Даффи,
9
Если вы действительно настаиваете на использовании, ==поместите его между [[и ]]. (И убедитесь, что первая строка вашего скрипта указывает на использование /bin/bash.)
holgero
18

Ребята: Несколько ответов показывают опасные примеры. В примере OP [ $a == $b ]специально использовалась подстановка переменных без кавычек (по состоянию на октябрь 17 г.). Для [...]что является безопасным для струнного равенства.

Но если вы собираетесь перечислять такие альтернативы, как [[...]], вы должны также сообщить, что правая часть должна быть процитирована. Если не в кавычках, это совпадение с образцом! (Из справочной страницы bash: «Любая часть шаблона может быть заключена в кавычки, чтобы заставить ее сопоставить ее как строку.»).

Здесь, в bash, два утверждения, дающие «да», соответствуют шаблону, остальные три - равенству строк:

$ 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
$
Б.К.-се
источник
2
Должен быть [ "$lht" = "$rht" ] в кавычках, чтобы быть надежным даже для равенства. Если у вас есть файл, созданный с помощью touch 'Afoo -o AB', [ $lft = $rht ]вернет true, даже если это имя файла совсем не идентично AB.
Чарльз Даффи