Как проверить, является ли переменная числом в Bash? - PullRequest
513 голосов
/ 30 апреля 2009

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

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

test *isnumber* $1 && VAR=$1 || echo "need a number"

Любая помощь?

Ответы [ 38 ]

694 голосов
/ 30 апреля 2009

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

re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
   echo "error: Not a number" >&2; exit 1
fi

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

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

... или, для обработки чисел со знаком:

^[+-]?[0-9]+([.][0-9]+)?$
248 голосов
/ 17 октября 2010

Без башмизмов (работает даже в Системе В Ш),

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

Это отклоняет пустые строки и строки, содержащие не-цифры, принимая все остальное.

Отрицательные числа или числа с плавающей точкой требуют дополнительной работы. Идея состоит в том, чтобы исключить - / . в первом «плохом» шаблоне и добавить больше «плохих» шаблонов, содержащих их нецелевое использование (?*-* / *.*.*)

166 голосов
/ 30 апреля 2009

Следующее решение также может использоваться в базовых оболочках, таких как 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
  • Большинство небашевых оболочек всегда будут оценивать это выражение как true
  • Поведение в Bash недокументировано и поэтому может меняться без предупреждения
  • Если значение включает пробелы после числа (например, «1 a»), возникает ошибка, например bash: [[: 1 a: syntax error in expression (error token is "a")
  • Если значение совпадает с var-name (например, i = "i"), выдает ошибку, например bash: [[: i: expression recursion level exceeded (error token is "i")
38 голосов
/ 24 апреля 2010

Этот тест проверяет, является ли число неотрицательным целым числом и является ли он независимым от оболочки (т.е. без искажений), и использует только встроенные функции оболочки:

НЕПРАВИЛЬНО.

Поскольку этот первый ответ (ниже) допускает целые числа с символами в них, пока первые не являются первыми в переменной.

[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";

ПРАВИЛЬНО .

Как прокомментировал jilles и предложил в свой ответ , это правильный способ сделать это, используя шаблоны оболочки.

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";
31 голосов
/ 26 октября 2012

Никто не предложил расширенного соответствия шаблонов bash :

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"
26 голосов
/ 14 июля 2012

Я удивлен решениями, непосредственно разбирающими числовые форматы в оболочке. shell не очень подходит для этого, поскольку является DSL для управления файлами и процессами. Немного ниже парсеры чисел, например:

isdecimal() {
  # filter octal/hex/ord()
  num=$(printf '%s' "$1" | sed "s/^0*\([1-9]\)/\1/; s/'/^/")

  test "$num" && printf '%f' "$num" >/dev/null 2>&1
}

Измените "% f" на тот формат, который вам необходим.

15 голосов
/ 04 мая 2013

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

Использование grep также прекрасно.
-E означает расширенное регулярное выражение
-q означает тихий (не эхо)
-qE является комбинацией обоих.

Для проверки непосредственно в командной строке:

$ echo "32" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is: 32

$ echo "3a2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is empty (false)

$ echo ".5" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer .5

$ echo "3.2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is 3.2

Использование в скрипте bash:

check=`echo "$1" | grep -E ^\-?[0-9]*\.?[0-9]+$`

if [ "$check" != '' ]; then    
  # it IS numeric
  echo "Yeap!"
else
  # it is NOT numeric.
  echo "nooop"
fi

Чтобы соответствовать JUST целым числам, используйте это:

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`
11 голосов
/ 01 марта 2014

Просто продолжение до @mary. Но из-за того, что у меня недостаточно представителей, я не смог опубликовать это как комментарий к этому сообщению. В любом случае, вот что я использовал:

isnum() { awk -v a="$1" 'BEGIN {print (a == a + 0)}'; }

Функция вернет "1", если аргумент является числом, в противном случае вернет "0" Это работает как для целых чисел, так и для чисел с плавающей точкой. Использование что-то вроде:

n=-2.05e+07
res=`isnum "$n"`
if [ "$res" == "1" ]; then
     echo "$n is a number"
else
     echo "$n is not a number"
fi
6 голосов
/ 21 октября 2011
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

Не забудьте - включить отрицательные числа!

6 голосов
/ 22 августа 2013

Старый вопрос, но я просто хотел воспользоваться своим решением. Этот не требует каких-либо странных трюков с оболочкой или полагается на то, чего не было всегда.

if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then
    echo 'is not numeric'
else
    echo 'is numeric'
fi

По сути, он просто удаляет все цифры из входных данных, и если у вас осталась строка не нулевой длины, то это не было число.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...