Чи є якась основна різниця при порівнянні змінної у вигляді рядка або як int


22

З цікавості під час порівняння баш-змінної (її значення є integer) можна перевірити її на певне заздалегідь визначене значення або оголошено як intабо як string.

Приклад сценарію :

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Вихід :

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

і

$  ./param.sh blah
$

Обидві функції поводяться однаково, і тому мені цікаво, чи є бажаний спосіб при перевірці цілочисленної змінної? Я б хотів перевірити intпорівняно, intяк це більш суворо, але мені цікаво, чи є якісь зворотні звороти для цього string?

У цьому випадку f2()також більш суворо ставиться до порівняння, тобто передача десяткового значення порушить його, тоді як це f1()буде приймати без проблем.


Зауважте, що Bash насправді не має тип даних Integer. Ви можете в основному просто натякнути Bash, щоб трактувати струну як ціле число.
helpermethod

Відповіді:


18

Так, багато відмінностей. Наприклад, =перевіряє точність рівності рядків, але -eqоцінює обидва вирази арифметично перед тим, як перевірити рівність:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Крім того, порожній рядок має числово рівний нулю:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

І зовсім інший клас відмінностей з'являється , коли ви приносите оператори порівняння в - з урахуванням <проти -lt, наприклад:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

Це відбувається тому, що рядок "2" знаходиться в алфавітному порядку після рядка "10" (оскільки 1 надходить до 2), але число "2" чисельно менше числа "10".


2
Не забувайте, що є також (( ... ))числові операції. (( " 1 " == 1 )) && echo yes || echo noрезультати вyes
Патрік

7

Порівняння цілого чи рядка стає найважливішим, коли ви порівнюєте більше чи менше:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

Перший виходить з ладу, оскільки 9 настає після 11 при сортуванні лексикографічно.

Зауважте, що використання лапок не визначає, чи порівнюєте ви рядки чи числа, оператор робить. Ви можете додати або видалити цитати вище, це не має ніякого значення. Bash вловлює невизначені змінні у подвійних дужках, тому лапки не потрібні. Використання лапок з одинарними дужками для чисельних тестів не допоможе вам заощадити:

[ "" -lt 11 ]

так чи інакше є помилкою ("ціле вираження потрібно"). Котирування - це ефективна захисна програма з порівнянням рядків в одинарних дужках:

[ "" \< 11 ]

Зауважте в подвійних дужках, ""буде, -eq 0але ні == 0.


1
У bash, не обов’язково цитувати змінні в подвійних дужках: вбудований [[досить розумний, щоб запам'ятати, де знаходяться змінні, і не буде обдурений порожніми змінними. Окремі дужки ( [) не мають цієї функції, і вони вимагають лапок.
glenn jackman

@glennjackman Не помічав цього. [[ -lt 11 ]]є помилкою, але nothing=; [[ $nothing -lt 11 ]]це не так. Я трохи переробив останній абзац.
goldilocks

2

Окрім сказаного.
Порівняння рівності швидше з числами, хоча в сценаріях оболонок рідко потрібно швидкий розрахунок.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s

Враховуючи, що вони роблять різні речі, я б сказав, що вистава не має значення - вам потрібно використовувати той, хто робить те, що ви хочете.
godlygeek

@godlygeek Порівняння рівності змінної може бути досягнуто в обох напрямках. "-eq" швидше.
Еммануель

Вони перевіряють на різні визначення рівності. Якщо ви хочете відповісти на питання "Чи містить ця змінна точний рядок 123", ви можете використовувати лише це =, оскільки використання -eqтакож відповідає "+123". Якби ви хотіли знати "Чи відповідає ця змінна, коли її оцінюють як арифметичний вираз, порівняти рівну 123", ви могли б використовувати лише її -eq. Єдиний раз, коли я можу побачити, де програмісту було б байдуже, яке визначення рівності було використано, це коли він знає, що вміст змінної обмежений певним шаблоном достроково.
godlygeek

@godlygeek Цікаво, що питання стосувалося порівняння рівності чисел, як якщо б вони були рядками, чи відповідає це випадку змінних, обмежених достроково певним шаблоном?
Еммануель

Ваш приклад ( b=234) відповідає цьому шаблону - ви знаєте, що це не +234 або "234" або "233 + 1", оскільки ви його призначили самостійно, тому ви знаєте, що він порівнює його як рядок і як число однаковою мірою. Але сценарій ОП, оскільки він вводить дані як аргумент командного рядка, не має такого обмеження - розгляньте його як ./param.sh 0+1або./param.sh " 1"
godlygeek
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.