Есть ли существенная разница при сравнении переменной в виде строки или целого числа?

22

Из любопытства, при выполнении сравнения переменных bash (его значение является 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()это не вызовет проблем.

fduff
источник
Обратите внимание, что Bash на самом деле не имеет тип данных Integer. Вы можете просто намекнуть Bash, чтобы рассматривать String как целое число.
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

И совсем другой класс различий появляется, когда вы вводите операторы сравнения - например, в сравнении с <vs -lt:

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

Это связано с тем, что строка «2» расположена в алфавитном порядке после строки «10» (поскольку 1 стоит перед 2), но число «2» численно меньше числа «10».

godlygeek
источник
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 нет необходимости заключать в кавычки переменные в двойных скобках: встроенный [[модуль достаточно умен, чтобы помнить, где находятся переменные, и он не будет одурачен пустыми переменными. Одиночные скобки ( [) не имеют этой функции и требуют кавычек.
Гленн Джекман
@glennjackman Не заметил этого. [[ -lt 11 ]]это ошибка, но nothing=; [[ $nothing -lt 11 ]]это не так. Я немного переработал последний абзац.
Златовласка
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 Сравнение равенства для переменной может быть достигнуто в обоих направлениях. "-экв" быстрее.
Эммануэль
Они проверяют разные определения равенства. Если вы хотите ответить на вопрос «Содержит ли эта переменная точную строку 123», вы можете использовать только ее =, так как использование также -eqбудет соответствовать «+123». Если вы хотите знать, «Если эта переменная при оценке в виде арифметического выражения сравнивается равной 123», вы можете использовать только -eq. Единственный раз, когда я могу увидеть, где программисту было бы все равно, какое определение равенства использовалось, это когда он знает, что содержимое переменной заранее ограничено определенным шаблоном.
Godlygeek
@godlygeek интересно, вопрос был о сравнении равенства чисел, как если бы они были строками, соответствует ли это случаю переменных, заранее ограниченных определенной моделью?
Эммануэль
Ваш пример ( b=234) соответствует этому шаблону - вы знаете, что это не +234 или «234» или «233 + 1», так как вы сами его присвоили, так что вы знаете, что сравнение его как строки и числа одинаково допустимо. Но сценарий OP, так как он принимает входные данные в качестве аргумента командной строки, не имеет такого ограничения - рассмотрите возможность назвать его как ./param.sh 0+1или./param.sh " 1"
godlygeek