Как проверить, является ли переменная числом в Bash?
Я никак не могу разобраться, как убедиться, что аргумент, передаваемый в мой скрипт, является числом или нет.
Я хочу сделать что-то вроде этого:
test *isnumber* $1 && VAR=$1 || echo "нужно число"
Не подскажете, как это реализовать?
5 ответ(ов)
Один из подходов заключается в использовании регулярного выражения, как показано ниже:
re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
echo "ошибка: Не число" >&2; exit 1
fi
Если значение не обязательно должно быть целым числом, вы можете изменить регулярное выражение следующим образом:
^[0-9]+([.][0-9]+)?$
...или, чтобы учесть числа со знаком:
^[+-]?[0-9]+([.][0-9]+)?$
Эти регулярные выражения позволят вам корректно проверять, является ли введенное значение числом или нет.
Этот код проверяет строку на наличие символов, отличных от цифр, и отвергает пустые строки. Если строка состоит только из цифр, выводится "good", иначе - "bad".
Вот пример кода:
case $string in
''|*[!0-9]*) echo bad ;;
*) echo good ;;
esac
Если необходимо обрабатывать отрицательные и вещественные числа, то нужно будет немного доработать проверку. Можно исключить символы -
и .
в первом "плохом" паттерне, а также добавить дополнительные "плохие" паттерны для неподобающего использования этих символов, например, ?*-*
для проверки на наличие минуса в начале и *.*.*
для проверки на множественные точки.
Для более сложной проверки, включающей поддержку отрицательных и вещественных чисел, можно использовать следующий подход:
case $string in
''|*[!0-9.-]*|*-*.*|*.*.*) echo bad ;;
*) echo good ;;
esac
В этом примере проверяются различные условия: пустая строка, наличие символов, отличных от цифр, знака минус в неподобающем месте и множественные точки. Если какое-либо из условий выполняется, выводится "bad", иначе - "good".
Следующее решение можно также использовать в базовых оболочках, таких как Bourne, без необходимости использования регулярных выражений. В основном, любые операции оценки числовых значений с использованием нечисловых данных приведут к ошибке, которая в оболочке будет неявно считаться ложной:
"$var" -eq "$var"
Вот пример:
#!/bin/bash
var=a
if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; then
echo number
else
echo not a number
fi
Вы также можете проверить код возврата ?
, что делает проверку более явной:
[ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null
if [ $? -ne 0 ]; then
echo "$var is not number"
fi
Перенаправление стандартного потока ошибок используется для скрытия сообщения "ожидалось целое число", которое Bash выводит в случае, если у нас нет числа.
ПРЕДУПРЕЖДЕНИЯ (спасибо за комментарии ниже):
- Числа с десятичными точками не идентифицируются как допустимые "числа".
- Использование
[[ ]]
вместо[ ]
всегда будет давать результатtrue
. - Большинство оболочек, отличных от Bash, всегда будут оценивать это выражение как
true
. - Поведение в Bash не документировано и может измениться без предупреждения.
- Если значение включает пробелы после числа (например, "1 a"), то возникнет ошибка, такая как
bash: [[: 1 a: синтаксическая ошибка в выражении (ошибочный токен "a")
. - Если значение совпадает с именем переменной (например,
i="i"
), то возникает ошибка, например,bash: [[: i: превышен уровень рекурсии выражения (ошибочный токен "i")
.
Я удивлён решениям, которые напрямую разбирают формат чисел в shell. Shell не совсем подходит для этой задачи, так как это язык специального назначения для управления файлами и процессами. Впрочем, существует множество парсеров чисел чуть ниже по уровню абстракции. Например:
isdecimal() {
# фильтруем восьмеричные/шестнадцатеричные числа и ord()
num=$(printf '%s' "$1" | sed "s/^0*\([1-9]\)/\1/; s/'/^/")
test "$num" && printf '%f' "$num" >/dev/null 2>&1
}
Замените '%f' на нужный вам формат.
Я посмотрел на ответы и... понял, что никто не учел числа с плавающей запятой (с точкой)!
Использование grep
тоже отличное решение.
-E
означает использование расширенных регулярных выражений.-q
означает тихий режим (не выводит результат).
Комбинация -qE
объединяет оба параметра.
Чтобы протестировать напрямую в командной строке, можете использовать следующий код:
$ echo "32" | grep -E ^\-?[0-9]?\.?[0-9]+$
# ответ: 32
$ echo "3a2" | grep -E ^\-?[0-9]?\.?[0-9]+$
# ответ пустой (ложный)
$ echo ".5" | grep -E ^\-?[0-9]?\.?[0-9]+$
# ответ: .5
$ echo "3.2" | grep -E ^\-?[0-9]?\.?[0-9]+$
# ответ: 3.2
Если вы хотите использовать это в bash-скрипте, можно сделать так:
check=`echo "$1" | grep -E ^\-?[0-9]*\.?[0-9]+$`
if [ "$check" != '' ]; then
# это числовое значение
echo "Да!"
else
# это не числовое значение
echo "Нет"
fi
Чтобы убедиться, что это только целые числа, используйте следующее:
# измените строку с проверкой на:
check=`echo "$1" | grep -E ^\-?[0-9]+$`
Таким образом, вы сможете проверять и числа с плавающей запятой, и целые числа.
Как выводить команды оболочки по мере их выполнения
Назначение значений по умолчанию для переменных оболочки одной командой в bash
Как указать приватный SSH-ключ для выполнения команды shell в Git?
Как сделать паузу в shell-скрипте на одну секунду перед продолжением?
Как использовать 'grep' для непрерывного потока?