Оператори рівності Bash (==, -eq)


136

Може хтось, будь-ласка, пояснить різницю між сценаріями -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-х років, який прийняв баш (та багато інших оболонок). У цьому і вся суть - якщо у вас є [[ взагалі , то можете сміливо припускати, що всі розширення ksh, реалізовані навколо неї ( fnmatch()-соответствие шаблону стилів, регулярні вирази ERE з =~, і так, придушення розбиття рядків і глобалізація файлової системи) буде в наявності. Оскільки [[синтаксис не POSIX в повному обсязі, додаткової втрати портативності немає, якщо припустити, що функції, з якими він народився, будуть доступні.
Чарльз Даффі

1
@DJCrashdummy ... посилання ви показуєте точки правильно , що лапки іноді необхідні на правій стороні від [[ $var = $pattern ]], якщо ви хочете , що б в іншому випадку можна інтерпретувати як шаблон fnmatch замість цього слід інтерпретувати як символьний рядок. Рядок fooне має нелітеральної інтерпретації, що робить залишення цитат абсолютно безпечним; це стосується лише того, що ОП хотіла б відповідати, скажімо, foo*(зірочкою як буквальною, не маючи на увазі нічого, що може з’явитися після рядка foo), то котирування або пробіг знадобиться.
Чарльз Даффі

@CharlesDuffy, на жаль, я поняття не маю, про що ти маєш на увазі, тому що мій коментар видається видаленим, і я не можу згадати, тому що це був досить довгий час. :-(
DJCrashdummy

@DJCrashdummy, ... так, я вважав, що ти сам його відкликав. По суті, це було заперечення про те, що котирування розширень всередині не [[були башизмом (вказуючи на те, що це була єдина причина, по якій ви не давали відповіді +1).
Чарльз Даффі

@CharlesDuffy ні, це не єдина причина: окрім того, що мені не подобаються bash-isms, ksh-isms і т. Д. Для простих завдань (це просто викликає неприємності і плутає початківців), я не розумію, чому змішувати сингл дужки [ ... ]і подвійний знак рівності ==. : - /
DJCrashdummy

27

Це залежить від конструкції тесту навколо оператора. Ваші варіанти - це подвійні дужки, подвійні дужки, одинарні дужки або тест

Якщо ви використовуєте ((...)) , ви тестуєте арифметичний власний капітал з, ==як у C:

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

(Примітка: 0означає trueв сенсі Unix і не нуль - це невдалий тест)

Використання -eqвсередині подвійних дужок є синтаксичною помилкою.

Якщо ви використовуєте [...] (або одиночну дужку) або [[...]] (або подвійну дужку), або testви можете використовувати один із -eq, -ne, -lt, -le, -gt або -ге як арифметичне порівняння .

$ [ 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

Причина в тому, що Баш нетипізований . -eqПризводить до того , рядках повинні інтерпретуватися як цілі числа , якщо це можливо , включаючи базову перетворення:

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

І 0якщо Баш вважає, що це просто рядок:

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

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

Останнє зауваження: Багато розширень Bash для тестових конструкцій не є POSIX, а тому не спрацюють з іншими оболонками. Інші снаряди зазвичай не підтримують [[...]]та ((...))або== .


Мені цікаво , про технічних причин для [[ "yes" -eq "no" ]]повернення True. Як bash примушує ці рядки до цілих значень, які можна порівняти? ;-)
одоні

4
Змінні Bash нетипізовані, тому [[ "yes" -eq "no" ]]еквівалентні [[ "yes" -eq 0 ]] або [[ "yes" -eq "any_noninteger_string" ]]- All True. Ці -eqсили цілого порівняння. "yes"Інтерпретується як ціле число 0; порівняння - Істинне, якщо інше ціле число є 0або результатом рядка є 0.
dawg

Бу, шипіння: показ ==у зразках коду (лише перенесення даних) та лише згадування (переносний, стандартизований) =під ним.
Чарльз Даффі

24

==є псевдонімом, характерним для bash, =і він виконує рядкове (лексичне) порівняння замість числового порівняння. eqяк числове порівняння, звичайно.

Нарешті, я зазвичай вважаю за краще використовувати форму if [ "$a" == "$b" ]


15
Використання ==тут є поганою формою, оскільки лише =визначено POSIX.
Чарльз Даффі

9
Якщо ви дійсно наполягаєте на використанні, ==тоді поставте їх між [[і ]]. (І переконайтеся, що перший рядок вашого сценарію вказаний для використання /bin/bash.)
holgero

18

Хлопці: Кілька відповідей показують небезпечні приклади. У прикладі ОП [ $a == $b ]спеціально використовується підстановка змінної без котирування (станом на жовтень 17 редагування). Бо [...]це безпечно для рівних рядків.

Але якщо ви збираєтесь перелічити подібні альтернативи [[...]], ви також повинні повідомити, що праворуч треба цитувати. Якщо не цитується, це відповідність шаблону! (З сторінки bash man: "Будь-яка частина шаблону може бути цитована, щоб змусити його відповідати як рядок.").

Тут у 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.
Чарльз Даффі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.