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

495

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

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

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

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

  • 17
    Как отступление - test && echo "foo" && exit 0 || echo "bar" && exit 1 подход test && echo "foo" && exit 0 || echo "bar" && exit 1 который вы используете, может иметь некоторые непреднамеренные побочные эффекты - если эхо не удается (возможно, вывод на закрытый FD), exit 0 будет пропущен, а затем код попытается echo "bar" . Если и при этом произойдет сбой, условие && не будет выполнено и даже не выполнит exit 1 ! Использование фактических операторов if вместо && / || менее склонен к неожиданным побочным эффектам.
  • 0
    @CharlesDuffy Это действительно умное мышление, к которому большинство людей приходят, только когда им приходится выслеживать волосатых жуков ...! Я никогда не думал, что эхо может вернуть неудачу.
Показать ещё 4 комментария
Теги:

37 ответов

664
Лучший ответ

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

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

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

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

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

^[+-]?[0-9]+([.][0-9]+)?$
  • 7
    +1 для этого подхода, но будьте осторожны с десятичными числами, выполняя этот тест, например, с ошибкой «1.0» или «1,0»: «Не число».
  • 12
    Я нашел '' exec> & 2; эхо ... '' довольно глупо. Просто "эхо ...> & 2"
Показать ещё 21 комментарий
225

Без багизмов (работает даже в System V sh),

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

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

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

  • 15
    +1 - это идиоматичный, переносимый способ возврата к исходной оболочке Bourne, со встроенной поддержкой подстановочных знаков в стиле glob. Если вы пришли из другого языка программирования, это выглядит жутко, но это гораздо элегантнее, чем справляться с хрупкостью различных проблем с цитированием и бесконечными проблемами обратной / боковой совместимости с if test ...
  • 6
    Вы можете изменить первую строку на ${string#-} (которая не работает в античных оболочках Борна, но работает в любой оболочке POSIX), чтобы принимать отрицательные целые числа.
Показать ещё 5 комментариев
152

Следующее решение может также использоваться в базовых оболочках, таких как 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"), выдает ошибку, например bash: [[: i: expression recursion level exceeded (error token is "i")
  • 7
    Я все еще рекомендую это (но с переменными в кавычках, чтобы учесть пустые строки), так как результат гарантированно будет использоваться как число в Bash, несмотря ни на что.
  • 19
    Будьте осторожны, чтобы использовать одинарные скобки; [[ a -eq a ]] оценивается как true (оба аргумента конвертируются в ноль)
Показать ещё 12 комментариев
34

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

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

НО НЕПРАВИЛЬНО.
Поскольку jilles прокомментировал и предложил в его ответ, это правильный способ сделать это с использованием shell-шаблонов.

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";
  • 4
    Это не работает должным образом, оно принимает любую строку, начинающуюся с цифры. Обратите внимание, что WORD в $ {VAR ## WORD} и аналогичных им является шаблоном оболочки, а не регулярным выражением.
  • 2
    Большое спасибо! Ответ обновлен.
Показать ещё 4 комментария
29

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

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"
  • 3
    Гленн, я удалил shopt -s extglob из твоего поста (за который я проголосовал, это один из моих любимых ответов здесь), так как в Условных конструкциях ты можешь читать: Когда используются операторы == и != , Строка справа от оператор считается шаблоном и сопоставляется в соответствии с правилами, описанными ниже в extglob Сопоставление шаблонов» , как если бы была extglob опция оболочки extglob . Надеюсь, ты не против!
  • 0
    В таких условиях вам не нужно shopt extglob ... это хорошо знать!
Показать ещё 8 комментариев
28

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

  • 4
    isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
  • 0
    Хорошее решение. printf действительно хорошо работает, даже ошибки с чем-то подходящим.
Показать ещё 12 комментариев
15

Я смотрел ответы и... понял, что никто не думал о числах 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 script:

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]+$`
  • 0
    Решения, использующие awk by triple_r и tripleee, работают с поплавками.
11

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

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

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

n=-2.05e+07
res=`isnum "$n"`
if [ "$res" == "1" ]; then
     echo "$n is a number"
else
     echo "$n is not a number"
fi
  • 3
    Печать числа менее полезна, чем установка кода выхода. 'BEGIN { exit(1-(a==a+0)) }' немного сложно найти, но его можно использовать в функции, которая возвращает true или false, как [ , grep -q и т. Д.
7

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html

Вы также можете использовать классы персонажей bash.

if [[ $VAR = *[[:digit:]]* ]]; then
 echo "$VAR is numeric"
else
 echo "$VAR is not numeric"
fi

Числа будут включать пробел, десятичную точку и "e" или "E" для числа с плавающей запятой.

Но если вы укажете шестнадцатеричное число в стиле C, то есть "0xffff" или "0XFFFF", [[: digit:]] вернет true. Здесь есть небольшая ловушка, bash позволяет вам делать что-то вроде "0xAZ00" и по-прежнему считать его цифрой (разве это не странная причуда компиляторов GCC, позволяющих использовать нотацию 0x для баз, отличных от 16??? )

Возможно, вы захотите проверить "0x" или "0X" перед проверкой, является ли оно числовым, если ваш ввод абсолютно ненадежен, если вы не хотите принимать шестнадцатеричные числа. Это будет достигнуто путем:

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi
  • 13
    [[ $VAR = *[[:digit:]]* ]] возвращает истину , если переменная содержит число, только если это не является целым числом.
  • 0
    [[ "z3*&" = *[[:digit:]]* ]] && echo "numeric" печатает numeric . Протестировано в bash версии 3.2.25(1)-release .
Показать ещё 3 комментария
6

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

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

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

  • 0
    Это не работает для пустой var .
  • 0
    Или для переменных с завершающими символами новой строки или что-то вроде $'0\n\n\n1\n\n\n2\n\n\n3\n' .
5

Не могу комментировать, поэтому я добавлю свой собственный ответ, который является расширением ответа glenn jackman с использованием соответствия шаблону bash.

Моя первоначальная необходимость заключалась в том, чтобы идентифицировать числа и отличать целые числа и поплавки. Определения функций вычитаются в:

function isInteger() {
    [[ ${1} == ?(-)+([0-9]) ]]
}

function isFloat() {
    [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}

Я использовал модульное тестирование (с shUnit2) для проверки моих шаблонов, работающих по назначению:

oneTimeSetUp() {
    int_values="0 123 -0 -123"
    float_values="0.0 0. .0 -0.0 -0. -.0 \
        123.456 123. .456 -123.456 -123. -.456
        123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
        123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
        123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
}

testIsIntegerIsFloat() {
    local value
    for value in ${int_values}
    do
        assertTrue "${value} should be tested as integer" "isInteger ${value}"
        assertFalse "${value} should not be tested as float" "isFloat ${value}"
    done

    for value in ${float_values}
    do
        assertTrue "${value} should be tested as float" "isFloat ${value}"
        assertFalse "${value} should not be tested as integer" "isInteger ${value}"
    done

}

Примечания. Шаблон isFloat может быть изменен, чтобы быть более толерантным относительно десятичной точки (@(.,)) и символа E (@(Ee)). Мои модульные тесты проверяют только значения, которые являются либо целыми, либо float, но не являются некорректным вводом.

  • 0
    Извините, я не понял, как должна использоваться функция редактирования.
5

Я бы попробовал это:

printf "%g" "$var" &> /dev/null
if [[ $? == 0 ]] ; then
    echo "$var is a number."
else
    echo "$var is not a number."
fi

Примечание: это распознает nan и inf как число.

  • 2
    или дубликат, или, возможно, лучше подходит как комментарий к ответу pixelbeat (использование %f , вероятно, лучше в любом случае)
  • 3
    Вместо того, чтобы проверять предыдущий код состояния, почему бы просто не поместить его в if ? Это то, что if делает ... if printf "%g" "$var" &> /dev/null; then ...
Показать ещё 1 комментарий
5
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

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

  • 0
    Какая минимальная версия bash? Я просто получаю bash: условный двоичный оператор, ожидаемый bash: синтаксическая ошибка рядом с неожиданным токеном `= ~ '
  • 1
    @PaulHargreaves =~ существовал по крайней мере еще в bash 3.0.
Показать ещё 2 комментария
4

Ясный ответ уже дал @charles Dufy и другие. Чистое решение bash будет использовать следующее:

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

Хотя для реальных чисел необязательно иметь число перед точкой счисления.

Чтобы обеспечить более полную поддержку плавающих чисел и научной нотации (многие программы в C/Fortran или иначе будут экспортировать float таким образом), полезным дополнением к этой строке будет следующее:

string="1.2345E-67"
if [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]?-?[0-9]+$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

Таким образом, приводя к способу дифференцировать типы чисел, если вы ищете какой-либо конкретный тип:

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+$ ]]
then
    echo $string is an integer
elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*$ ]]
then
    echo $string is a float
elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]-?[0-9]+$ ]]
then
    echo $string is a scientific number
else
    echo $string is not a number
fi

Примечание. Мы могли бы указать синтаксические требования для десятичной и научной нотации, одним из которых является запятая в качестве точки счисления, а также ".". Тогда мы утверждали бы, что должна быть только одна такая точка счисления. В плавателе [Ee] могут быть два знака +/-. Я изучил еще несколько правил работы Aulu и протестировал их против плохих строк, таких как '' '-' '-E-1' '0-0'. Вот мои инструменты regex/substring/expr, которые, кажется, поднимаются вверх:

parse_num() {
 local r=`expr "$1" : '.*\([.,]\)' 2>/dev/null | tr -d '\n'` 
 nat='^[+-]?[0-9]+[.,]?$' \
 dot="${1%[.,]*}${r}${1##*[.,]}" \
 float='^[\+\-]?([.,0-9]+[Ee]?[-+]?|)[0-9]+$'
 [[ "$1" == $dot ]] && [[ "$1" =~ $float ]] || [[ "$1" =~ $nat ]]
} # usage: parse_num -123.456
4
test -z "${i//[0-9]}" && echo digits || echo no no no

${i//[0-9]} заменяет любую цифру в значении $i пустой строкой, см. man -P 'less +/parameter\/' bash. -z проверяет, имеет ли результирующая строка нулевую длину.

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

test -n "$i" && test -z "${i//[0-9]}" && echo digits || echo not a number
[[ -n "$i" && -z "${i//[0-9]}" ]] && echo digits || echo not a number
4

Я использую expr. Он возвращает ненулевое значение, если вы пытаетесь добавить ноль к нечисловому значению:

if expr -- "$number" + 0 > /dev/null 2>&1
then
    echo "$number is a number"
else
    echo "$number isn't a number"
fi

Возможно, можно использовать bc, если вам нужны нецелые числа, но я не верю, что bc ведет себя точно так же. Добавление нуля к нечисленному номеру возвращает вас к нулю и также возвращает значение нуля. Может быть, вы можете объединить bc и expr. Используйте bc чтобы добавить ноль к $number. Если ответ 0, то попробуйте expr чтобы убедиться, что $number не равно нулю.

  • 1
    Это довольно плохо. Чтобы сделать его немного лучше, используйте expr -- "$number" + 0 ; все же это будет все еще делать вид, что 0 isn't a number . От man expr : Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
3

Как я должен был вмешаться в это в последнее время и, как и appart с karttu с unit test больше всего. Я пересмотрел код и добавил некоторые другие решения, попробуйте сами, чтобы увидеть результаты:

#!/bin/bash

    # N={0,1,2,3,...} by syntaxerror
function isNaturalNumber()
{
 [[ ${1} =~ ^[0-9]+$ ]]
}
    # Z={...,-2,-1,0,1,2,...} by karttu
function isInteger() 
{
 [[ ${1} == ?(-)+([0-9]) ]]
}
    # Q={...,-½,-¼,0.0,¼,½,...} by karttu
function isFloat() 
{
 [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
    # R={...,-1,-½,-¼,0.E+n,¼,½,1,...}
function isNumber()
{
 isNaturalNumber $1 || isInteger $1 || isFloat $1
}

bools=("TRUE" "FALSE")
int_values="0 123 -0 -123"
float_values="0.0 0. .0 -0.0 -0. -.0 \
    123.456 123. .456 -123.456 -123. -.456 \
    123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
    123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
    123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
false_values="blah meh mooh blah5 67mooh a123bc"

for value in ${int_values} ${float_values} ${false_values}
do
    printf "  %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value)
    printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value)
    printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value)
    printf "%5s=%-24s\n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value)
done

So isNumber() включает тире, запятые и экспоненциальные обозначения и, следовательно, возвращает TRUE для целых чисел и floats, где, с другой стороны isFloat() возвращает FALSE для целочисленных значений, а isInteger() также возвращает FALSE в поплавках. Для вашего удобства все как один вкладыш:

isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]]; }
isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; }
isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; }
  • 0
    Лично я бы удалил ключевое слово function как оно не делает ничего полезного. Кроме того, я не уверен насчет полезности возвращаемых значений. Если не указано иное, функции будут возвращать состояние выхода последней команды, поэтому вам не нужно ничего return самостоятельно.
  • 0
    Хорошо, действительно, return значения сбивают с толку и делают его менее читабельным. Использование function ключевых слов или нет - это больше вопрос личного вкуса, по крайней мере, я удалил их из одних строк, чтобы сэкономить место. Спасибо.
Показать ещё 4 комментария
3

Самый простой способ - проверить, содержит ли он символы, отличные от цифр. Вы заменяете все цифровые символы ничем и проверяете длину. Если длина не равна числу.

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
  • 2
    Для обработки отрицательных чисел потребуется более сложный подход.
  • 0
    ... или необязательный положительный знак.
Показать ещё 1 комментарий
2

Это может быть достигнуто с помощью grep чтобы увидеть, соответствует ли рассматриваемая переменная расширенному регулярному выражению.

Тест целое число 1120:

yournumber=1120
if [ $(echo "$yournumber" | grep -qE '^[0-9]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Выход: Valid number.

Тест нецелого числа 1120a:

yournumber=1120a
if [ $(echo "$yournumber" | grep -qE '^[0-9]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Вывод: Error: not a number.


объяснение

  • grep, переключатель -E позволяет нам использовать расширенное регулярное выражение '^[0-9]+$'. Это регулярное выражение означает, что переменная должна содержать только [] числа от 0-9 нуля от девяти до ^ конца переменной $ и должна содержать как минимум + один символ.
  • grep, тихий переключатель -q отключает любой выход независимо от того, находит ли он что-либо.
  • $? это статус выхода предыдущей выполненной команды. Состояние выхода 0 означает успех, а что-либо большее означает ошибку. Команда grep имеет статус выхода 0 если находит совпадение, и 1 если нет;
  • $() - это подоболочка, которая позволяет нам выполнить другую команду и затем использовать вывод.

Так положить все это вместе, в $() подоболочки, мы echo переменной $yournumber и | -q его в grep который с переключателем -q молча соответствует расширенному регулярному выражению -E '^[0-9]+$'. Затем мы echo $? статус выхода, который будет 0 если grep успешно нашел совпадение, и 1 если нет.

Теперь, за пределами подоболочки $() и обратно в условное условие if, мы берем вывод, либо 0 либо 1 из подоболочки $() и проверяем, не равен ли он -ne "0". Если он не соответствует, статус выхода будет 1 который не соответствует "0". Затем мы echo "Error: not a number." , При успешном совпадении выходной сигнал состояния выхода будет равен 0 что равно "0" и в этом случае мы echo "Valid number." ,


Для поплавков или пар

Мы можем просто изменить регулярное выражение с '^[0-9]+$' на '^[0-9]*+\.?[0-8]+$' для чисел с плавающей запятой или двойных чисел.

Тест поплавок 1120.01:

yournumber=1120.01
if [ $(echo "$yournumber" | grep -qE '^[0-9]*+\.?[0-8]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Выход: Valid number.

Тест поплавок 11.20.01:

yournumber=11.20.01
if [ $(echo "$yournumber" | grep -qE '^[0-9]*+\.?[0-8]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Вывод: Error: not a number.


Для негативов

Чтобы разрешить отрицательные целые числа, просто измените регулярное выражение с '^[0-9]+$' на '^\-?[0-9]+$'.

Чтобы разрешить отрицательные числа с плавающей запятой или двойные числа, просто измените регулярное выражение с '^[0-9]*+\.?[0-8]+$' на '^\-?[0-9]*+\.?[0-8]+$'.

1

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

re="^[0-9]*[.]{0,1}[0-9]*$"

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi
  • 0
    Не могли бы вы объяснить, почему ваш ответ в корне отличается от других старых ответов, например, ответа Чарльза Даффи? Ну, ваш ответ на самом деле не работает, поскольку он проверяет один период .
  • 0
    Я не уверен, что понимаю единственный период здесь ... это один или нулевой период, ожидаемый .... Но верно, ничего принципиально иного, просто нашел регулярное выражение легче для чтения.
Показать ещё 2 комментария
1

Следуйте за David W answer от октября '13, если использовать expr, это может быть лучше

test_var=`expr $am_i_numeric \* 0` >/dev/null 2>&1
if [ "$test_var" = "" ]
then
    ......

Если числовое значение, умноженное на 1, дает одно и то же значение (включая отрицательные числа). В противном случае вы получите null, который вы можете проверить для

  • 0
    expr - зверь, которого трудно приручить. Я не тестировал это решение, но я бы не использовал expr в пользу современных встроенных оболочек, если только совместимость с устаревшими оболочками начала 1980-х не является важным требованием.
1
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

Удалите -\? в шаблоне соответствия grep, если вы не принимаете отрицательное целое число.

  • 2
    Даунвот за отсутствие объяснений. Как это работает? Он выглядит сложным и хрупким, и неясно, какие именно данные он примет. (Например, действительно ли необходимо удалить пробелы? Почему? Он скажет, что число со встроенными пробелами является допустимым числом, что может быть нежелательно.)
1

Мне нравится ответ Альберто Заккагни.

if [ "$var" -eq "$var" ] 2>/dev/null; then

Важные предпосылки: - не было порожних подоболочек - не активированы парсеры RE - большинство приложений оболочки не используют реальные числа

Но если $var является сложным (например, ассоциативный доступ к массиву), и если число будет неотрицательным целым числом (большинство случаев использования), то это, пожалуй, более эффективно?

if [ "$var" -ge 0 ] 2> /dev/null; then ..
1

Я использую printf в качестве других упомянутых ответов, если вы укажете строку формата "% f" или "% i", printf проверит вас. Легче, чем переосмысливать проверки, синтаксис прост и короток, а printf вездесущ. По моему мнению, это достойный выбор - вы также можете использовать следующую идею, чтобы проверить ряд вещей, что не только полезно для проверки чисел.

declare  -r CHECK_FLOAT="%f"  
declare  -r CHECK_INTEGER="%i"  

 ## <arg 1> Number - Number to check  
 ## <arg 2> String - Number type to check  
 ## <arg 3> String - Error message  
function check_number() { 
  local NUMBER="${1}" 
  local NUMBER_TYPE="${2}" 
  local ERROR_MESG="${3}"
  local -i PASS=1 
  local -i FAIL=0   
  case "${NUMBER_TYPE}" in 
    "${CHECK_FLOAT}") 
        if ((! $(printf "${CHECK_FLOAT}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
    "${CHECK_INTEGER}") 
        if ((! $(printf "${CHECK_INTEGER}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
                     *) 
        echo "Invalid number type format: ${NUMBER_TYPE} to check_number()." 1>&2
        echo "${FAIL}"
        ;;                 
   esac
} 

>$ var=45

>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }

1

Вы также можете использовать "let":

[ ~]$ var=1
[ ~]$ let $var && echo "It a number" || echo "It not a number"
It\ a number
[ ~]$ var=01
[ ~]$ let $var && echo "It a number" || echo "It not a number"
It\ a number
[ ~]$ var=toto
[ ~]$ let $var && echo "It a number" || echo "It not a number"
It\ not a number
[ ~]$ 

Но я предпочитаю использовать оператор "= ~" Bash 3+, как некоторые ответы в этом потоке.

  • 5
    Это очень опасно. Не оценивайте непроверенную арифметику в оболочке. Это должно быть проверено другим способом в первую очередь.
1

Чтобы уловить отрицательные числа:

if [[ $1 == ?(-)+([0-9.]) ]]
    then
    echo number
else
    echo not a number
fi
  • 3
    Это подтвердит 1.2.3.4.5.6.7.8 или -........
  • 0
    Кроме того, для этого необходимо сначала включить расширенную глобализацию. Это функция только для Bash, которая по умолчанию отключена.
1
  • для проверки

    number=12345 или number=-23234 или number=23.167 или number=-345.234

  • проверить числовые или нецифровые

    echo $number | grep -E '^-?[0-9]*\.?[0-9]*$' > /dev/null

  • принять решение о дальнейших действиях, основанных на статусе выхода выше.

    if [ $? -eq 0 ]; then echo "Numeric"; else echo "Non-Numeric"; fi

1

Я нашел довольно короткую версию:

function isnum()
{
    return `echo "$1" | awk -F"\n" '{print ($0 != $0+0)}'`
}
  • 0
    хм .. разве это не просто возвращает 0, если строка не является числом? Означает ли это, что он не работает, если ваша строка "0" ?
  • 0
    @ naught101 Совершенно верно :)
1

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

function isInteger {
  [ $(($1+0)) != 0 ] && echo "$1 is a number" || echo "$1 is not a number"
 }

x=1;      isInteger $x
x="1";    isInteger $x
x="joe";  isInteger $x
x=0x16 ;  isInteger $x
x=-32674; isInteger $x   

$(($ 1 + 0)) вернет 0 или бомбу, если $1 НЕ является целым числом. для примера:

function zipIt  { # quick zip - unless the 1st parameter is a number
  ERROR="not a valid number. " 
  if [ $(($1+0)) != 0 ] ; then  # isInteger($1) 
      echo " backing up files changed in the last $1 days."
      OUT="zipIt-$1-day.tgz" 
      find . -mtime -$1 -type f -print0 | xargs -0 tar cvzf $OUT 
      return 1
  fi
    showError $ERROR
}

ПРИМЕЧАНИЕ. Думаю, я никогда не думал проверять наличие поплавков или смешанных типов, которые сделают всю бомбу script... в моем случае я не хотел, чтобы это продвигалось дальше. Я собираюсь поиграть с решением mrucci и регулярным выражением Duffy - они кажутся наиболее надежными в рамках bash...

  • 3
    Это принимает арифметические выражения, такие как 1+1 , но отклоняет некоторые натуральные числа с ведущими 0 с (потому что 08 - недопустимая восьмеричная константа).
  • 1
    Это также имеет другие проблемы: 0 не является числом, и это подвергается произвольному внедрению кода, попробуйте это: isInteger 'a[$(ls)]' . По электронной почте Ой.
Показать ещё 1 комментарий
1

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

[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"

Он удаляет каждый символ: digit: class в $var и проверяет, осталось ли мы с пустой строкой, что означает, что оригинал был только числом.

Что мне нравится в этом, так это его небольшая занимательность и гибкость. В этой форме он работает только для целых чисел без разделителей, основанных на базе 10, хотя, конечно, вы можете использовать сопоставление шаблонов в соответствии с другими потребностями.

  • 0
    Читая решение mrucci, оно выглядит почти так же, как и мое, но с использованием обычной замены строк вместо «sed style». Оба используют одни и те же правила для сопоставления с образцом и, AFAIK, являются взаимозаменяемыми решениями.
1

Я использую следующее (для целых чисел):

## ##### constants
##
## __TRUE - true (0)
## __FALSE - false (1)
##
typeset -r __TRUE=0
typeset -r __FALSE=1

## --------------------------------------
## isNumber
## check if a value is an integer 
## usage: isNumber testValue 
## returns: ${__TRUE} - testValue is a number else not
##
function isNumber {
  typeset TESTVAR="$(echo "$1" | sed 's/[0-9]*//g' )"
  [ "${TESTVAR}"x = ""x ] && return ${__TRUE} || return ${__FALSE}
}

isNumber $1 
if [ $? -eq ${__TRUE} ] ; then
  print "is a number"
fi
  • 0
    Почти правильно (вы принимаете пустую строку), но излишне сложно до такой степени запутывать.
  • 1
    Неправильно: вы принимаете -n и т. Д. (Из-за echo ), и вы принимаете переменные с завершающими символами новой строки (из-за $(...) ). И, кстати, print не является допустимой командой оболочки.
0

Если у вас установлен Perl, этот вкладыш прост, читабелен и расширяем

perl -se 'exit($n !~ /\d+/)' -- -n=a

Вот несколько тестов

perl -se 'exit($n !~ /\d+/)' -- -n=a; echo $?
1
perl -se 'exit($n !~ /\d+/)' -- -n=2; echo $?
0

Это довольно само собой объяснил, но здесь больше информации

  • -e включает оценку
  • -s позволяет передавать аргументы после - в этом случае -n
  • !~ является оператором совпадения регулярного выражения отрицания, так как 0 в bash - это успех, мы хотим, чтобы он успешно -n если аргумент -n является числом

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

is_number() { perl -se 'exit($n !~ /^\d+(\.\d+)?$/)' -- -n="$1"; }
0

Числа могут быть рассчитаны, поэтому запустите номера через калькулятор.

var=81
if (( $(echo "$var == $var" | bc -l 2>/dev/null) )); then
    echo "It a number"
else
    echo "not a number"
fi

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

-2

Стек выскочил из сообщения, спросил меня, действительно ли я хочу ответить после 30+ ответов? Но конечно!!! Используйте bash новые функции, и вот оно: (после комментария я внес изменения)

function isInt() {([[$ 1 -eq $(($ 1 + 0))]] 2 > /dev/null && & && & [[$ 1!= '']] && & эхо 1) || echo ''}

function isInt() {
   ([[ $1 =~ ^[-+0-9]+$  ]] && [[ $1 -eq $(( $1 + 0 )) ]] 2>/dev/null && [[ $1 != '' ]] && echo 1) || echo ''
}

Поддержка:

===============out-of-the-box==================
 1. negative integers (true & arithmetic),
 2. positive integers (true & arithmetic),
 3. with quotation (true & arithmetic),
 4. without quotation (true & arithmetic),
 5. all of the above with mixed signs(!!!) (true & arithmetic),
 6. empty string (false & arithmetic),
 7. no value (false & arithmetic),
 8. alphanumeric (false & no arithmetic),
 9. mixed only signs (false & no arithmetic),
================problematic====================
 10. positive/negative floats with 1 decimal (true & NO arithmetic),
 11. positive/negative floats with 2 or more decimals (FALSE & NO arithmetic).

True/false - это то, что вы получаете из функции только при использовании в сочетании с подстановкой процесса, например, в [[ $( isInt <arg> ) ]], поскольку в bash нет логического типа нет возвращаемого значения функции.

Я использую капитал, когда результат тестового выражения WRONG, тогда как он должен быть обратным!

Посредством "арифметики" я имею в виду, что bash может выполнять математику, как в этом выражении: $x=$(( $y + 34)).

Я использую "арифметику/без арифметики", когда в математических выражениях аргумент действует так, как ожидается, и "НЕТ арифметики", когда он ошибочно работает по сравнению с ожидаемым.

Как вы видите, только 10 и 11 являются проблематичными!

Perfect!

PS: Обратите внимание, что МОСТ популярный ответ не работает в случае 9!

  • 0
    Нет, пожалуйста, нет !!! это полностью сломано! и это подлежит выполнению произвольного кода. Попробуйте сами: isInt 'a[$(ls>&3)]' 3>&1 . Вы рады, что я использовал ls только как команду (которая выполняется дважды). И ваш код также утверждает, что a[$(ls>&3)] - это число.
  • 0
    Не говоря уже о том, что он будет утверждать, что любое допустимое имя переменной (которое не установлено или расширяется до числа, либо пустое, либо рекурсивно расширяется до числа, либо пустое) является числом. Например, unset a; isInt a
Показать ещё 2 комментария
-2

Это немного грубо по краям, но немного более дружелюбный к новичкам.

if [ $number -ge 0 ]
then
echo "Continue with code block"
else
echo "We matched 0 or $number is not a number"
fi

Это приведет к ошибке и напечатает "Недопустимый номер:", если число $number не является числом, но оно не выйдет из script. Как ни странно, нет тестовой опции, которую я мог бы найти, чтобы просто проверить целое число. Логика здесь будет соответствовать любому числу, которое больше или равно 0.

  • 2
    Ваш тест пропускает 0, не говоря уже о отрицательных числах.
  • 0
    кроме того, для этого требуется [[ ]] вместо [ ] , в противном случае произойдет сбой в нецелых строках.
Показать ещё 2 комментария
-3

Принятый ответ здесь не работает, я нахожусь на MacOS. Работает следующий код:

if [ $(echo "$number" | grep -c '^[0-9]\+$') = 0 ]; then 
    echo "it is a number"
else
    echo "not a number"
fi
  • 4
    У этого есть много проблем: самая очевидная из них состоит в том, что вы должны сравнивать с 1 а не с 0 в утверждении теста. Другая проблема в том, что если переменная number содержит символы новой строки: number=$'42\nthis is not a number, right?' будет подтверждено
  • 3
    А? Принятый ответ действительно работает на MacOS. Пожалуйста, прокомментируйте это с деталями увиденной проблемы и (в идеале) репродуктором. (Я подозреваю, что, возможно, он запускался в /bin/sh - либо через #!/bin/sh shebang, либо с помощью скрипта, sh scriptname с sh scriptname - вместо bash).
Показать ещё 1 комментарий
-3

Ниже написан Script и используется для интеграции Script с Nagios, и он работает до сих пор

#!/bin/bash
# Script to test variable is numeric or not
# Shirish Shukla
# Pass arg1 as number
a1=$1
a=$(echo $a1|awk '{if($1 > 0) print $1; else print $1"*-1"}')
b=$(echo "scale=2;$a/$a + 1" | bc -l 2>/dev/null)
if [[ $b > 1 ]]
then
    echo "$1 is Numeric"
else
    echo "$1 is Non Numeric"
fi

EG:

# sh isnumsks.sh   "-22.22"
-22.22 is Numeric

# sh isnumsks.sh   "22.22"
22.22 is Numeric

# sh isnumsks.sh   "shirish22.22"
shirish22.22 is Non  Numeric
  • 8
    Это сложно и сломано. В echo "$a1" вам нужны двойные кавычки, в противном случае подстановочные знаки в строке раскрываются, и результат зависит от того, какие файлы находятся в текущем каталоге (попробуйте isnumsks.sh "*" , затем повторите попытку после создания файла с именем 42 ). Вы смотрите только на первое слово, разделенное пробелами, поэтому 42 psych неправильно классифицируется как числовое. Все виды ввода, которые не являются числовыми, но имеют действительный синтаксис bc могут испортить это, например, 2012-01-03 .

Ещё вопросы

Сообщество Overcoder
Наверх
Меню