9

Как проверить, является ли переменная числом в Bash?

5

Я никак не могу разобраться, как убедиться, что аргумент, передаваемый в мой скрипт, является числом или нет.

Я хочу сделать что-то вроде этого:

test *isnumber* $1 && VAR=$1 || echo "нужно число"

Не подскажете, как это реализовать?

5 ответ(ов)

11

Один из подходов заключается в использовании регулярного выражения, как показано ниже:

re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
   echo "ошибка: Не число" >&2; exit 1
fi

Если значение не обязательно должно быть целым числом, вы можете изменить регулярное выражение следующим образом:

^[0-9]+([.][0-9]+)?$

...или, чтобы учесть числа со знаком:

^[+-]?[0-9]+([.][0-9]+)?$

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

4

Этот код проверяет строку на наличие символов, отличных от цифр, и отвергает пустые строки. Если строка состоит только из цифр, выводится "good", иначе - "bad".

Вот пример кода:

case $string in
    ''|*[!0-9]*) echo bad ;;
    *) echo good ;;
esac

Если необходимо обрабатывать отрицательные и вещественные числа, то нужно будет немного доработать проверку. Можно исключить символы - и . в первом "плохом" паттерне, а также добавить дополнительные "плохие" паттерны для неподобающего использования этих символов, например, ?*-* для проверки на наличие минуса в начале и *.*.* для проверки на множественные точки.

Для более сложной проверки, включающей поддержку отрицательных и вещественных чисел, можно использовать следующий подход:

case $string in
    ''|*[!0-9.-]*|*-*.*|*.*.*) echo bad ;;
    *) echo good ;;
esac

В этом примере проверяются различные условия: пустая строка, наличие символов, отличных от цифр, знака минус в неподобающем месте и множественные точки. Если какое-либо из условий выполняется, выводится "bad", иначе - "good".

2

Следующее решение можно также использовать в базовых оболочках, таких как 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").
0

Я удивлён решениям, которые напрямую разбирают формат чисел в 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' на нужный вам формат.

0

Я посмотрел на ответы и... понял, что никто не учел числа с плавающей запятой (с точкой)!

Использование 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]+$`

Таким образом, вы сможете проверять и числа с плавающей запятой, и целые числа.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь