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

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

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

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

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


person Flávio Amieiro    schedule 30.04.2009    source источник
comment
В стороне - подход test && echo "foo" && exit 0 || echo "bar" && exit 1, который вы используете, может иметь некоторые непреднамеренные побочные эффекты - если эхо не удается (возможно, вывод на закрытый FD), exit 0 будет пропущен, а затем код будет пытаться echo "bar". Если это тоже не удается, условие && не выполняется, и оно даже не будет выполняться exit 1! Использование фактических операторов if вместо _7 _ / _ 8_ менее подвержено неожиданным побочным эффектам.   -  person Charles Duffy    schedule 24.08.2011
comment
@CharlesDuffy Это действительно умное мышление, к которому большинство людей приходит только тогда, когда им нужно выслеживать волосатых жуков ...! Я никогда не думал, что эхо может вернуть неудачу.   -  person Camilo Martin    schedule 25.06.2014
comment
Немного поздно на вечеринку, но я знаю об опасностях, о которых писал Чарльз, потому что мне тоже пришлось пройти через них некоторое время назад. Итак, вот вам на 100% защищенная от дурака (и хорошо читаемая) строка: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; } Фигурные скобки указывают на то, что вещи НЕ должны выполняться в подоболочке (что определенно было бы так, если бы вместо них использовались () круглые скобки). Предупреждение: Никогда не пропускайте последнюю точку с запятой. В противном случае вы можете заставить bash распечатать самые уродливые (и самые бессмысленные) сообщения об ошибках ...   -  person syntaxerror    schedule 09.06.2015
comment
Это не работает в Ubuntu, если вы не удалите кавычки. Так что это должно быть просто [[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO   -  person Treviño    schedule 10.09.2015
comment
... Я удалил (неверный) ответ из вопроса - часть того, почему ответы должны быть отдельными, заключается в том, чтобы их можно было комментировать, голосовать, исправлять и т. Д. Индивидуально.   -  person Charles Duffy    schedule 27.04.2016
comment
Вам нужно будет уточнить, что вы подразумеваете под числом. Целое число? Число с фиксированной точкой? Научное (е) обозначение? Есть ли требуемый диапазон (например, 64-битное значение без знака) или вы разрешаете записывать любое число?   -  person Toby Speight    schedule 15.11.2016
comment
Невероятно, что bash не предоставляет надежный встроенный способ проверки того, является ли значение числовым. Количество, разнообразие и различия в качестве ответов здесь указывают на то, что это серьезная проблема.   -  person Michael Burr    schedule 30.07.2020
comment
Bash действительно предоставляет надежные средства определения того, является ли число INTEGER. {VAR = asdfas; ((VAR)); echo $ ?; } Уравнение не будет корректным, если ответ «0», потому что «0» не является целым числом. У меня была такая же проблема всего несколько минут назад, и я нашел эту тему с помощью быстрого поиска. Надеюсь, это поможет другим. Хотя другие люди были близки.   -  person That one guy from the movie    schedule 27.06.2021


Ответы (40)


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

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

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

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

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

^[+-]?[0-9]+([.][0-9]+)?$
person Charles Duffy    schedule 30.04.2009
comment
+1 для этого подхода, но будьте осторожны с десятичными знаками, выполняя этот тест, например, с ошибкой 1,0 или 1,0: не число. - person sourcerebels; 30.04.2009
comment
@lhunath - это правда. Я склонен использовать его в более сложных обработчиках ошибок (т.е. гораздо больше, чем просто одно эхо), но здесь эта привычка просочилась. - person Charles Duffy; 02.05.2009
comment
^-*[0-9]+([.][0-9]+)?$, чтобы также проверить отрицательные числа - person Ben; 24.06.2011
comment
@ Бен, вы действительно хотите обрабатывать более одного знака минус? Я бы сделал это ^-?, а не ^-*, если вы на самом деле не выполняете работу по правильной обработке нескольких инверсий. - person Charles Duffy; 27.06.2011
comment
@SandraSchlichting Отправляет весь будущий вывод на stderr. На самом деле здесь нет смысла, потому что здесь только одно эхо, но это привычка, которую я обычно использую для случаев, когда сообщения об ошибках охватывают несколько строк. - person Charles Duffy; 17.10.2012
comment
@Charles Duffy Можно также рассмотреть случай с плавающей точкой в ​​форме .1234 без начального нуля с ^[0-9]*([.][0-9]+)?$. Это потребует дополнительной проверки непустой строки жестко .. - person Legionair; 21.05.2013
comment
При полной проверке if ! [[ -n "$yournumber" && "$yournumber" =~ ^-?[0-9]*([.][0-9]+)?$ ]]; then echo NAN; fi - person Legionair; 21.05.2013
comment
Я не уверен, почему регулярное выражение должно быть сохранено в переменной, но если это сделано для совместимости, я не думаю, что это необходимо. Вы можете просто применить выражение напрямую: [[ $yournumber =~ ^[0-9]+$ ]]. - person konsolebox; 01.09.2013
comment
@konsolebox да, совместимость. Обработка обратной косой черты в буквальных регулярных выражениях в правой части =~ изменилась между 3.1 и 3.2, тогда как обработка обратной косой черты в присваиваниях постоянна во всех соответствующих выпусках bash. Таким образом, следуя практике постоянного присвоения переменных регулярных выражений перед сопоставлением с ними с помощью =~, можно избежать сюрпризов. Я делаю это здесь, чтобы научить хорошим привычкам, даже несмотря на то, что это конкретное регулярное выражение не имеет escape-символов обратной косой черты. - person Charles Duffy; 01.09.2013
comment
@CharlesDuffy И это, собственно, причина, по которой я предпочел бы использовать расширенное глобальное отображение, которое существует с 2.05b или, может быть, раньше. =~ начался с 3.0 и с тех пор имел разные реализации. Что на самом деле наиболее сложно, так это то, что вам все равно придется заключать в кавычки символы в значениях переменных, которые вы намеревались предоставить как буквальные (например, ${VAR//./\\.}), чтобы они не анализировались как символы регулярного выражения, тогда как с == вы можете просто поместить их в "", например. +(x)"$V". И единственная причина, по которой я мог использовать его поверх доп. шаблоны - это когда я мог использовать BASH_REMATCH. - person konsolebox; 01.09.2013
comment
@konsolebox Что касается самой сложной части, есть другие способы справиться с этой проблемой. [[ $s =~ $re_start"$literal"$re_end ]], например, обрабатывает развернутое содержимое "$literal" как буквальное, экранирование не требуется. (Я также нахожу [.] значительно более читаемым и более легким в обращении, чем \., но, по общему признанию, это велосипедное шеддинг) - person Charles Duffy; 01.09.2013
comment
@konsolebox ... кстати, я не утверждаю, что здесь есть только один правильный ответ. Я также поддержал ответ jilles (хотя для завершения требуется подтвержденный бит расширения). - person Charles Duffy; 01.09.2013
comment
@CharlesDuffy [[ $s =~ $re_start"$literal"$re_end ]] будет работать так же, как ==, начиная с версии 3.2, но не в версии 3.0 или 3.1. [[ $s =~ (x) ]] без необходимости экономить на переменной также будет работать только начиная с 4.0. Расширенные шаблоны нужно активировать только один раз в начале скрипта, так что это не имеет особого значения. Он также совместим со всеми версиями bash, начиная с 2.05b. Ну, эти последние мои комментарии уже касались того, почему кто-то предпочел бы расширенные шаблоны =~. Информация также может быть полезна другим читателям, возможно, если они заботятся о совместимости. - person konsolebox; 01.09.2013
comment
@Hans, то вы пытаетесь использовать его с оболочкой, отличной от bash 3.x или новее. Обратите внимание, что /bin/sh - это POSIX sh (или, в древних системах, оболочка Bourne), а не bash, даже если это символическая ссылка на bash. - person Charles Duffy; 06.12.2013
comment
Я сделал if [[ 123 =~ '^[0-9]+$' ]]; then echo good; fi и ничего не получил. Но re='^[0-9]+$'; if [[ 123 =~ $re ]]; then echo good; fi сказал good. Почему? Должен ли я что-то убегать в первой версии? - person Frozen Flame; 01.08.2014
comment
@FrozenFlame, помещая правую часть =~ в кавычки, делает его больше не регулярным выражением, а регулярной строкой (в современных версиях bash, но не в некоторых древних), тогда как версия, которую я дал в этом ответе, работает последовательно в каждом выпуске где =~ вообще поддерживается. - person Charles Duffy; 01.08.2014
comment
А как насчет того, когда число начинается с десятичной точки, например .5? Вместо этого я бы использовал ^-?([0-9]*[.])?[0-9]+$. - person HelloGoodbye; 03.06.2015
comment
Почему!, = ~ И [[]]? Может кто-нибудь объяснить компоненты if / else? - person LP_640; 07.10.2015
comment
@ LP_640, это не компоненты if - вы можете использовать все эти конструкции вне контекста if. Остальное см. В BashFAQ № 31 (mywiki.wooledge.org/BashFAQ/031); помимо того, что там описано, ! инвертирует статус выхода команды (делает правду ложной, а ложь - истинной); это распространенный способ написания не на многих-многих языках. - person Charles Duffy; 11.10.2015
comment
@JMS, см. str="hello world"; re='[[:space:]]'; if [[ $str =~ $re ]]; then echo "matched"; fi ожидаемое поведение на странице ideone.com/K7kM6M. - person Charles Duffy; 21.07.2019
comment
@JMS, ... Интересно, включает ли ваша среда выполнения eval или какой-либо другой этап предварительного выполнения. Можете ли вы придумать пример, который не работает без кавычек, и опубликовать ссылку на его выполнение на ideone.com? - person Charles Duffy; 21.07.2019
comment
@CharlesDuffy углубился в это еще немного, и вы правы, в любом случае. не уверен, что происходило раньше. Спасибо за ваши Коментарии. удалил другие мои комментарии. кстати ... не удалось заставить ideone.com принять ввод. это вещь? - person JMS; 22.07.2019
comment
Единственная сложность (для меня) в реализации stdin в ideone заключается в том, что они не реализуют завершающий символ новой строки, необходимый для действительных текстовых файлов UNIX, а скорее идут по пути DOS / Windows и помещают символы новой строки между строк, но не после последней, поэтому read не работает для последней строки, введенной в поле stdin (что можно исправить, поместив пустую строку в конец поля stdin). Однако, кроме этого, у меня исторически не было проблем. - person Charles Duffy; 22.07.2019
comment
Имейте в виду, что десятичная дробь начинается с точки типа .3 или заканчивается точкой типа 3., это НЕ будет работать. Я хотел бы предложить использовать ^[+-]?([0-9]*[.])?([0-9]+)?$, однако сначала нужно исключить три особых случая: ., +., -.. - person Xiang; 12.09.2019
comment
Я пытался учесть начальные и конечные пробелы, как в x = 123. Поэтому я попытался изменить re = '^ \ s * [0-9] + \ s * $'. Но он никогда не совпадает с числом с ведущими и / или конечными пробелами ... - person Gabriel; 10.06.2020
comment
кажется, работает не цитировать его, если вы не хотите объявлять переменную re: if [[123a = ~ ^ [0-9] + $]]; тогда эхо верно; иначе эхо ложь; фи - person AmanicA; 30.06.2020
comment
@AmanicA, оно не должно быть заключено в кавычки с правой стороны независимо от того, исходит ли оно от переменной. Использование переменных в качестве рекомендуемой практики предназначено для обратной совместимости со старыми версиями bash в тех случаях, когда ваше регулярное выражение содержит обратную косую черту, как описано в ветке комментариев выше. - person Charles Duffy; 30.06.2020
comment
Поскольку я новичок в bash, мне интересно, есть ли какая-то конкретная причина, по которой оператор if в первом примере не может быть таким if [[ ! $yournumber =~ $re ]]? - person Marco Dufal; 02.07.2020
comment
@MarcoDufal, может быть, это просто потому, что я еще не пил кофе, но чем это отличается от того, что есть уже есть? - person Charles Duffy; 02.07.2020
comment
@CharlesDuffy ну просто оператор отрицания ! не находится в квадратных скобках в вашем фрагменте кода. - person Marco Dufal; 02.07.2020
comment
Попался. Оба совершенно законны; Мне нравится сохранять отрицание во внешней области, где это по привычке отражает смысл, поэтому более сложные условия легче читать. Например, рассмотрите [[ ! $foo && $bar ]]; читателю необходимо подумать, применяется ли ! только к $foo или к результату операции И обоих; поместите его снаружи, и с первого взгляда станет очевидным, что отрицание применимо ко всему тесту, а не только к одной его ветви. - person Charles Duffy; 02.07.2020
comment
Или для обработки целых чисел, у которых нет ведущего нуля ^[1-9][0-9]*$ - person Jesse Chisholm; 25.07.2020
comment
Вот почему я использую python, когда это возможно. - person jerrylogansquare; 11.12.2020
comment
Разве мы не должны использовать return вместо exit 1, который также завершает сеанс ssh - person alper; 28.05.2021
comment
@alper, зависит от контекста. Я предполагал, что это будет использоваться в сценарии, который, как ожидается, завершится, если будут заданы недопустимые аргументы. Если вы используете его в функции, да, используйте return. - person Charles Duffy; 28.05.2021
comment
@CharlesDuffy Пожалуйста, взгляните на мой полностью отредактированный ответ (есть более подходящая плавающая версия решения с регулярным выражением) - person F. Hauri; 24.06.2021

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

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

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

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

person jilles    schedule 16.10.2010
comment
+1 - это идиоматический переносимый способ вернуться к исходной оболочке Bourne и имеет встроенную поддержку подстановочных знаков в стиле глобуса. Если вы пришли с другого языка программирования, это выглядит жутковато, но это намного элегантнее, чем справляться с хрупкостью различных проблем с цитированием и бесконечными проблемами обратной / боковой совместимости с if test ... - person tripleee; 04.09.2011
comment
Вы можете изменить первую строку на ${string#-} (которая не работает в античных оболочках Bourne, но работает в любой оболочке POSIX), чтобы принимать отрицательные целые числа. - person Gilles 'SO- stop being evil'; 03.01.2012
comment
Кроме того, это легко распространить на числа с плавающей запятой - просто добавьте '.' | *.*.* к запрещенным шаблонам и добавьте точку к разрешенным символам. Точно так же вы можете разрешить необязательный знак раньше, хотя тогда я бы предпочел case ${string#[-+]} просто игнорировать знак. - person tripleee; 07.06.2014
comment
См. Это для обработки целых чисел со знаком: stackoverflow.com/a/18620446/2013911 - person Niklas Peter; 13.12.2015
comment
Разве это не должно быть case "$string" in вместо case $string in? - person Dor; 06.10.2016
comment
@Dor Кавычки не нужны, поскольку команда case в любом случае не выполняет разбиение слов и генерацию пути для этого слова. (Однако при раскрытии шаблонов case может потребоваться цитирование, поскольку оно определяет, являются ли символы сопоставления шаблонов буквальными или специальными.) - person jilles; 07.10.2016
comment
Объяснение символа вертикальной черты (вертикальной черты): заголовок unix.stackexchange.com/questions/85939/ - person Roland; 19.02.2020
comment
Кажется, это самый быстрый способ! Взгляните на мое сравнение - person F. Hauri; 25.06.2021

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

"$var" -eq "$var"

as in:

#!/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: syntax error in expression (error token is "a")
  • Если значение совпадает с именем переменной (например, i = "i"), выдает ошибку, например bash: [[: i: expression recursion level exceeded (error token is "i")
person Alberto Zaccagni    schedule 30.04.2009
comment
Я бы по-прежнему рекомендовал это (но с переменными, указанными для пустых строк), поскольку результат гарантированно будет пригодным для использования как число в Bash, несмотря ни на что. - person l0b0; 24.12.2010
comment
Позаботьтесь об использовании одинарных скобок; [[ a -eq a ]] оценивается как истина (оба аргумента преобразуются в ноль) - person Tgr; 28.08.2012
comment
Это действительно очень удобно и работает в bash (который был запрошен) и в большинстве других оболочек, но, к сожалению, не в ksh. Если вы хотите быть портативным, используйте раствор jilles. - person Adrian Frühwirth; 04.05.2013
comment
Очень хорошо! Обратите внимание: это работает только для целого числа, а не для любого числа. Мне нужно было проверить один аргумент, который должен быть целым числом, поэтому это сработало: if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then - person haridsv; 02.08.2013
comment
Это великолепно. Спасибо. - person Kyle Strand; 04.09.2013
comment
Я бы настоятельно не советовал использовать этот метод из-за немалого количества оболочек, [ встроенных в которых будет оценивать аргументы как арифметические. Это верно как для ksh93, так и для mksh. Кроме того, поскольку оба этих массива поддерживают массивы, существует простая возможность для внедрения кода. Вместо этого используйте сопоставление с образцом. - person ormaaj; 08.10.2014
comment
@AlbertoZaccagni, в текущих выпусках bash эти значения интерпретируются с помощью правил числового контекста только для [[ ]], но не для [ ]. Тем не менее, это поведение не указано ни в стандарте POSIX для test, ни в собственной документации bash; будущие версии bash могут изменять поведение в соответствии с ksh без нарушения каких-либо задокументированных поведенческих обещаний, поэтому надежда на сохранение текущего поведения не гарантирует безопасность. - person Charles Duffy; 26.03.2016
comment
Ах, понятно, спасибо за объяснение. Я не особо беспокоился об этом 7 лет назад, когда писал это: D - person Alberto Zaccagni; 28.03.2016
comment
Строго говоря, это не совсем недокументировано. man test показывает, что -eq - для целых чисел, а = - для строк. страница руководства подразумевает, но явно не говорит, что -eq не сможет выполнить сравнение строк. - person JDS; 02.09.2016
comment
Как можно это отрицать? - person pfnuesel; 24.02.2018
comment
@pfnuesel ! [ "$var" -eq "$var" ]. Причина: -ne возвращает ненулевое значение для нецелочисленных аргументов. - person Tom Hale; 04.09.2018
comment
Вам нужно будет проверить, что $var не равно нулю. Я предлагаю отредактировать: [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null - person Tom Hale; 04.09.2018
comment
@TomHale и Альберто, $ {x: -1} -eq $ {x: -0} защищает от пустого ввода. - person 0andriy; 04.10.2018
comment
@AlbertoZaccagni Вы действительно должны изменить свой ответ, чтобы четко указать, что он работает только для целых чисел, т.е. измените все упоминания чисел на целые числа. Я знаю, что вы намекнули на это в своем разделе с предостережениями, но вы используете общий номер слова повсюду в основной части вашего сообщения, что неверно с математической (теории множеств) точки зрения и вводит в заблуждение неопытного пользователя. - person Atralb; 05.11.2020

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

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"

или используя новый стиль POSIX:

[[ $1 == ?(-)+([:digit:]) ]] && echo "$1 is an integer"
person glenn jackman    schedule 26.10.2012
comment
Гленн, я удаляю shopt -s extglob из вашего сообщения (за которое я проголосовал, это один из моих любимых ответов здесь), поскольку в Условные конструкции, вы можете прочитать: Когда используются операторы == и !=, строка справа от оператора считается шаблоном и сопоставляется в соответствии с правилами описано ниже в сопоставлении с шаблоном, как если бы extglob была включена опция оболочки. Надеюсь, вы не против! - person gniourf_gniourf; 13.02.2015
comment
В таких случаях вам не нужно _1 _... это хорошо знать! - person gniourf_gniourf; 13.02.2015
comment
Хорошо работает для простых целых чисел. - person ; 09.03.2015
comment
Ваше решение не работает В 3.2.25(1)-release bash: -bash: syntax error in conditional expression: unexpected token ('-bash: синтаксическая ошибка рядом с `? (-'. В этом выпуске функция, отмеченная @gniourf_gniourf, не работает. - person Jdamian; 08.09.2016
comment
@gniourf_gniourf, в отличие от расширенного сопоставления с образцом, не работает в версии 3.2.25, как вы цитировали, простое сопоставление с образцом работает, например, [[ x3x = *[[:digit:]]* ]], даже с одним = оператор. В справочных страницах 3.2 bash строка as if the extglob shell option were enabled не найдена. - person Jdamian; 08.09.2016
comment
@Jdamian: вы правы, это было добавлено в Bash 4.1 (который был выпущен в конце 2009 года ... Bash 3.2 был выпущен в 2006 году ... теперь это антикварная программа, извините тех, кто застрял в прошлом). Кроме того, вы можете возразить, что extglobs были введены в версии 2.02 (выпущенной в 1998 году) и не работают в ‹2.02 версиях… Теперь ваш комментарий здесь послужит предупреждением относительно более старых версий. - person gniourf_gniourf; 08.09.2016
comment
Ответ был бы сильнее, если бы он дал немного больше объяснений. - person studgeek; 06.01.2017
comment
@glennjackman: Как избежать отрицательных чисел, используя тот подход, который вы используете? Мне нужны только положительные числа. - person Destructor; 15.11.2017
comment
@Destructor, удалите бит ?(-), который необязательно соответствует начальному дефису, поэтому вы просто сопоставляете цифры с +([0-9]) - person glenn jackman; 15.11.2017
comment
Это работало для меня в bash 5.0.11, но разве не должно быть "$1" вместо $1? или не имеет значения в этом случае? - person downtheroad; 14.01.2020
comment
Переменные в [[...]] не подлежат разделению на слова или расширению глобуса. - person glenn jackman; 14.01.2020
comment
тоже мой любимый ответ, я чувствую, что было бы неплохо подробнее объяснить; также кажется, что это не обе руки [[$ 1 ==? (-) + ([0-9])]] - ›работает [[? (-) + ([0-9]] == $ 1]] ] - ›не сработало (по крайней мере, у меня ubuntu 18) - person Thiago Conrado; 08.12.2020
comment
@ThiagoConrado, найдите [[...]] в руководстве ( или help [[ в приглашении bash): только правая часть == является шаблоном. - person glenn jackman; 08.12.2020
comment
Как бы вы отменили это, чтобы проверить нецелое число? - person Hashim Aziz; 29.05.2021

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

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

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

[ -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";
person mrucci    schedule 24.04.2010
comment
Это не работает должным образом, принимает любую строку, начинающуюся с цифры. Обратите внимание, что WORD в $ {VAR ## WORD} и подобных ему является шаблоном оболочки, а не регулярным выражением. - person jilles; 17.10.2010
comment
Вы можете перевести это выражение на английский, пожалуйста? Я действительно хочу его использовать, но я недостаточно понимаю его, чтобы доверять ему, даже после просмотра страницы руководства bash. - person CivFan; 26.05.2016
comment
*[!0-9]* - это шаблон, который соответствует всем строкам с хотя бы одним нецифровым символом. ${num##*[!0-9]*} - это расширение параметра, в котором мы берем содержимое переменной num и удаляем самую длинную строку, соответствующую шаблону. Если результат расширения параметра не пустой (! [ -z ${...} ]), то это число, поскольку оно не содержит каких-либо нецифровых символов. - person mrucci; 26.05.2016
comment
К сожалению, это не удается, если в аргументе есть какие-либо цифры, даже если это недопустимое число. Например instance1ple или a2b. - person studgeek; 06.01.2017
comment
Оба терпят неудачу с 122s :-( - person Hastur; 06.04.2018

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

person pixelbeat    schedule 14.07.2012
comment
Хорошее решение. printf действительно хорошо работает, даже с ошибками с чем-то подходящим. - person dpb; 27.02.2013
comment
@sputnick ваша версия нарушает присущую (и полезную) семантику возвращаемого значения исходной функции. Поэтому вместо этого просто оставьте функцию как есть и используйте ее: isnumber 23 && echo "this is a number" || echo "not a number" - person michael; 19.07.2013
comment
Разве у этого не должно быть также 2>/dev/null, чтобы isnumber "foo" не загрязнял stderr? - person gioele; 06.06.2014
comment
Вызов современных оболочек, таких как bash, DSL для управления файлами и процессами, означает игнорирование того факта, что они используются для гораздо большего, чем это - некоторые дистрибутивы построили на нем целые менеджеры пакетов и веб-интерфейсы (как бы уродливо это ни было). Однако пакетные файлы соответствуют вашему описанию, поскольку даже установить там переменную сложно. - person Camilo Martin; 26.06.2014
comment
Забавно, что вы пытаетесь быть умным, копируя некоторые идиомы с других языков. К сожалению, в оболочках это не работает. Оболочки очень особенные, и, не зная о них, вы, скорее всего, напишете неработающий код. Ваш код не работает: isnumber "'a" вернет истину. Это задокументировано в спецификации POSIX, где вы прочитаете: < i> Если ведущий символ - это одинарная или двойная кавычка, значение должно быть числовым значением в базовом кодовом наборе символа, следующего за одинарной или двойной кавычкой. - person gniourf_gniourf; 13.02.2015
comment
Скорее всего, ваш код сломается и в других случаях. Я только что дал одну. Я надеюсь, что 27 парней, проголосовавших за, не использовали это в критически важном производственном коде. :D. - person gniourf_gniourf; 13.02.2015
comment
Другой случай, когда это не удается: isinteger() { [[ $1 ]] && printf '%d' "$1" >/dev/null 2>&1; } не удастся, например, isinteger 09: это из-за глупого начального 0. Теперь вы можете возразить, что 09 не следует проверять ... как бы то ни было, isnumber "'a" уже является хорошим доказательством того, что вся эта конструкция нарушена. - person gniourf_gniourf; 13.02.2015
comment
Обратите внимание на функцию isnumber (). Если вам нужен более строгий isdecimal (), вы можете фильтровать с помощью sed "s/^0*//; s/'/^/" - person pixelbeat; 15.02.2015
comment
Я знаю, что функция была isnumber (функция isinteger, которую я дал, - это просто (неудачная) попытка обобщения вашего метода). Забавно, но теперь 0 - это не число. Ваша функция теперь порождает 4 подоболочки, использует внешнюю команду, не работает. И вы утверждаете, что оболочка - это просто DSL для управления файлами и процессами? вам нужно изучить лучшие методы работы с оболочкой, и когда вы будете более осведомлены, вы увидите, что единственный разумный способ решить эту проблему - это действительно проанализировать строку. - person gniourf_gniourf; 15.02.2015
comment
Кстати, вы неправильно используете printf. Например, ваша функция проверяет 42\n\n\n\n\n\n\n\n\n\n\n\n\n, %s42%s или %d9 - person gniourf_gniourf; 15.02.2015
comment
Намного лучше! тем не менее, он по-прежнему проверяет переменные, содержащие завершающие символы новой строки: $'1\n\n\n'; это потому, что $(...) обрезает завершающие символы новой строки. Что бы ни. Вы находитесь в точке, где вы критикуете методы, которые явно анализируют строку (и поэтому мы ожидаем, что ваш метод будет действительно лучше): ваш метод порождает 3 подоболочки, даже не на 100% безопасен (в отношении завершающих символов новой строки), каким-то образом анализирует строка с sed намного менее эффективна, чем методы, которые непосредственно анализируют строку. Молодец. - person gniourf_gniourf; 15.02.2015
comment
Не могли бы вы добавить пример того, как вызвать это и использовать его результат? - person Lightness Races in Orbit; 17.06.2015
comment
Чуть ниже есть множество парсеров чисел, но вы не используете их, а реализуете собственное решение на основе регулярных выражений. Поскольку в bash есть собственные регулярные выражения, что именно он покупает? - person Charles Duffy; 26.03.2016
comment
@pixelbeat У меня серьезные проблемы с пониманием смысла использования s/'/^/. Не могли бы вы уточнить это? Я не понимаю, как замена ' на ^ будет иметь какой-либо смысл (это то, что делает sed, когда ^ один) при рассмотрении десятичного входного числа. - person Atralb; 05.11.2020
comment
Замена 'на ^ заменяет то, что printf интерпретирует как число, на то, что он не делает. Т.е. Обычно мы не хотим рассматривать 'blah как число, поэтому измените его так, чтобы printf отклонил - person pixelbeat; 06.11.2020

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

Чтобы сопоставить ТОЛЬКО целые числа, используйте это:

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`
person Sergio Abreu    schedule 04.05.2013
comment
Решения, использующие awk от triple_r и tripleee, работают с числами с плавающей запятой. - person Ken Jackson; 27.08.2017
comment
Спасибо за это и очень хороший момент! Потому что вопрос в том, как проверить, является ли это числом, а не просто целым числом. - person Tanasis; 01.04.2020
comment
Я тоже благодарю вас, Танасис! Давайте всегда помогать друг другу. - person Sergio Abreu; 11.04.2020

(2-й) Полный текст этого ответа: 27 июня 2021 г.

Некоторые советы по производительности и совместимости

Есть несколько сильно различающихся методов в отношении разных видов тестов.

Я рассмотрел наиболее подходящие методы и построил это сравнение.

Беззнаковое целое is_uint()

Эти функции реализуют код для оценки того, является ли выражение целым числом без знака, т. Е. Полностью ли оно состоит из цифр.

  • Использование расширения параметров

    (Это был мой подход до всего этого!)

    isuint_Parm() { [ "$1" ] && [ -z "${1//[0-9]}" ] ;}
    
  • Использование вилки для grep

    isuint_Grep() { grep -qE '^[0-9]+$' <<<"$1"; }
    

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

  • Использование целочисленных возможностей bash < / strong>

    isuint_Bash() { (( 10#$1 >= 0 )) 2>/dev/null ;}
    
  • Использование case

    isuint_Case() { case $1 in ''|*[!0-9]*) return 1;;esac;}
    
  • Использование регулярного выражения bash

    isuint_Regx() { [[ $1 =~ ^[0-9]+$ ]] ;}
    

Целое число со знаком is_int()

Эти функции реализуют код для оценки того, является ли выражение целым числом со знаком, т. Е. Как указано выше, но разрешая необязательный знак перед числом.

  • Использование расширения параметров

    isint_Parm() { local chk=${1#[+-]}; [ "$chk" ] && [ -z "${chk//[0-9]}" ] ;}
    
  • Использование целочисленных возможностей bash < / strong>

    isint_Bash() { (( 10#$1 )) 2>/dev/null ;}
    
  • Использование case

    isint_Case() { case ${1#[-+]} in ''|*[!0-9]*) return 1;;esac;}
    
  • Использование регулярного выражения bash

    isint_Regx() { [[ $1 =~ ^[+-]?[0-9]+$ ]] ;}
    

Число (без знака с плавающей запятой) is_num()

Эти функции реализуют код для оценки того, является ли выражение числом с плавающей запятой, т. Е. Как указано выше, но разрешая необязательную десятичную точку и дополнительные цифры после нее. Это не пытается охватить числовые выражения в экспоненциальной нотации (например, 1.0234E-12).

  • Использование расширения параметров

    isnum_Parm() { local ck=${1#[+-]};ck=${ck/.};[ "$ck" ]&&[ -z "${ck//[0-9]}" ];}
    
  • Использование регулярного выражения bash

    isnum_Regx() { [[ $1 =~ ^[+-]?([0-9]+([.][0-9]*)?|\.[0-9]+)$ ]] ;}
    
  • Использование case

    isnum_Case() { case ${1#[-+]} in ''|.|*[!0-9.]*|*.*.*) return 1;; esac ;}
    

Тесты концептов

(Вы можете скопировать / вставить этот тестовый код после ранее объявленных функций.)

testcases=(
    1 42 -3 +42 +3. .9 3.14 +3.141 -31.4 '' . 3-3 3.1.4 3a a3 blah 'Good day!'
);printf '%-12s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n' Function \
       U{Prm,Grp,Bsh,Cse,Rgx} I{Prm,Bsh,Cse,Rgx} N{Prm,Cse,Rgx}; \
for var in "${testcases[@]}";do
    outstr='';
    for func in isuint_{Parm,Grep,Bash,Case,Regx} isint_{Parm,Bash,Case,Regx} \
                       isnum_{Parm,Case,Regx};do
        if $func "$var"
        then outstr+='  num'
        else outstr+='  str'
        fi
    done
    printf '%-11s %s\n' "|$var|" "$outstr"
done

Должен выводить:

Function     UPrm UGrp UBsh UCse URgx IPrm IBsh ICse IRgx NPrm NCse NRgx
|1|           num  num  num  num  num  num  num  num  num  num  num  num
|42|          num  num  num  num  num  num  num  num  num  num  num  num
|-3|          str  str  str  str  str  num  num  num  num  num  num  num
|+42|         str  str  num  str  str  num  num  num  num  num  num  num
|+3.|         str  str  str  str  str  str  str  str  str  num  num  num
|.9|          str  str  str  str  str  str  str  str  str  num  num  num
|3.14|        str  str  str  str  str  str  str  str  str  num  num  num
|+3.141|      str  str  str  str  str  str  str  str  str  num  num  num
|-31.4|       str  str  str  str  str  str  str  str  str  num  num  num
||            str  str  num  str  str  str  str  str  str  str  str  str
|.|           str  str  str  str  str  str  str  str  str  str  str  str
|3-3|         str  str  num  str  str  str  str  str  str  str  str  str
|3.1.4|       str  str  str  str  str  str  str  str  str  str  str  str
|3a|          str  str  str  str  str  str  str  str  str  str  str  str
|a3|          str  str  str  str  str  str  str  str  str  str  str  str
|blah|        str  str  str  str  str  str  str  str  str  str  str  str
|Good day!|   str  str  str  str  str  str  str  str  str  str  str  str

Я надеюсь! (Примечание: uint_bash кажутся не идеальными!)

Сравнение производительности

Затем я построил эту тестовую функцию:

testFunc() {
    local tests=1000 start=${EPOCHREALTIME//.}
    for ((;tests--;)) ;do
        "$1" "$3"
    done
    printf -v "$2" %u $((${EPOCHREALTIME//.}-start))
}
percent(){ local p=00$((${1}00000/$2));printf -v "$3" %.2f%% ${p::-3}.${p: -3};}
sortedTests() {
    local func NaNTime NumTime ftyp="$1" nTest="$2" tTest="$3" min i pct line
    local -a order=()
    shift 3
    for func ;do
        testFunc "${ftyp}_$func" NaNTime "$tTest"
        testFunc "${ftyp}_$func" NumTime "$nTest"
        order[NaNTime+NumTime]=${ftyp}_$func\ $NumTime\ $NaNTime
    done
    printf '%-12s %11s %11s %14s\n' Function Number NaN Total
    min="${!order[*]}" min=${min%% *}
    for i in "${!order[@]}";do
        read -ra line <<<"${order[i]}"
        percent "$i" "$min" pct
        printf '%-12s %9d\U00B5s %9d\U00B5s  %12d\U00B5s  %9s\n' \
               "${line[@]}" "$i" "$pct"
    done
}

Я мог бежать так:

sortedTests isuint "This is not a number." 31415926535897932384 \
            Case Grep Parm Bash Regx ;\
sortedTests isint  "This is not a number." 31415926535897932384 \
            Case Parm Bash Regx ;\
sortedTests isnum "This string is clearly not a number..." \
            3.141592653589793238462643383279502884  Case Parm Regx

На моем хосте это показывает:

Function          Number         NaN          Total
isuint_Case       6762µs      8492µs         15254µs    100.00%
isuint_Bash      13478µs     12739µs         26217µs    171.87%
isuint_Parm      11324µs     18807µs         30131µs    197.53%
isuint_Regx      20777µs     27616µs         48393µs    317.25%
isuint_Grep    1516390µs   1491751µs       3008141µs  19720.34%
Function          Number         NaN          Total
isint_Case        8630µs      8042µs         16672µs    100.00%
isint_Bash       14254µs     12272µs         26526µs    159.10%
isint_Parm       16445µs     20491µs         36936µs    221.54%
isint_Regx       23661µs     28287µs         51948µs    311.59%
Function          Number         NaN          Total
isnum_Case        9579µs     10328µs         19907µs    100.00%
isnum_Parm       21115µs     28983µs         50098µs    251.66%
isnum_Regx       35552µs     58453µs         94005µs    472.22%

Заключение

  • case путь явно самый быстрый! Примерно в 3 раза быстрее, чем regex, и в 2 раза быстрее, чем при использовании расширения параметров.
  • вилок (для grep или любых двоичных файлов) следует избегать, когда они не нужны.

case стал моим любимым выбором:

is_uint() { case $1        in '' | *[!0-9]*              ) return 1;; esac ;}
is_int()  { case ${1#[-+]} in '' | *[!0-9]*              ) return 1;; esac ;}
is_unum() { case $1        in '' | . | *[!0-9.]* | *.*.* ) return 1;; esac ;}
is_num()  { case ${1#[-+]} in '' | . | *[!0-9.]* | *.*.* ) return 1;; esac ;}

О совместимости

Для этого я написал небольшой тестовый сценарий, основанный на предыдущих тестах, с:

for shell in bash dash 'busybox sh' ksh zsh "$@";do
    printf "%-12s  " "${shell%% *}"
    $shell < <(testScript) 2>&1 | xargs
done

Это показывает:

bash          Success
dash          Success
busybox       Success
ksh           Success
zsh           Success

Насколько мне известно, другое решение на основе bash, например регулярное выражение и целое число bash не будет работать во многих других оболочках, а вилки требуют больших ресурсов, я бы предпочитайте способ case (непосредственно перед расширением параметров, которое также в большинстве случаев совместимо).

person F. Hauri    schedule 16.05.2020
comment
Я согласен, в любом случае, я предпочитаю не использовать регулярное выражение, когда я мог бы использовать расширение параметров ... Злоупотребление RE замедлит работу сценария bash - person F. Hauri; 21.05.2020
comment
@CharlesDuffy, на моем raspberry pi 1000 итераций займет 2,5 секунды с моей версией и 4,4 секунды с вашей версией! - person F. Hauri; 21.05.2020
comment
С этим не поспоришь. ???? - person Charles Duffy; 21.05.2020
comment
TIL set -- может использоваться для установки позиционных параметров - person fire; 24.07.2020
comment
Не могли бы вы также включить case ответ в сравнение? Он получил мой голос как за простоту, так и за элегантность. В моих тестах это значительно быстрее, чем обе ваши альтернативы. В IdeOne это менее очевидно, но все же быстрее: ideone.com/AVvMOU - person tripleee; 24.06.2021
comment
@tripleee Указанный вами скрипт глючит! casenum не работают с плавающими числами! И его тест не использует его $fun переменную !! Но я согласен, я мог бы обратить внимание на этот способ работы! - person F. Hauri; 24.06.2021
comment
Спасибо за ответ; обновил фрагмент. Извините за глупую опечатку. Случай case (sic) теперь явно быстрее и на IdeOne. (Я должен был проверить ...) Хотя, вероятно, лучший тест не только проверял бы успех. - person tripleee; 24.06.2021
comment
Уже работаю над: _1 _... вроде хорошо! - person F. Hauri; 24.06.2021
comment
@tripleee ответ переписан. - person F. Hauri; 24.06.2021
comment
Этот скрипт можно найти там: isnum_comparission.sh - person F. Hauri; 27.06.2021
comment
Возможно, обратите внимание, что isuint_Bash немного быстрее, чем case в NaN тесте. Если вы находитесь в замкнутом цикле, где учитывается каждый цикл, и ожидаете, что вводимые данные в основном недействительны, это может изменить сравнение в пользу версии с расширением параметров. Но только для случая uint. Возможно, разница настолько мала, что в любом случае находится в пределах погрешности измерения. - person tripleee; 28.06.2021
comment
@tripleee Я не понимаю. Это выглядит крошечной разницей из-за того, что тесты проводились на моем личном столе, который не молчит. Выполнение этого теста (из моего скрипта) много раз покажет эту разницу случайным образом в другом способ.,, - person F. Hauri; 28.06.2021
comment
Тогда должен ли тест охватывать больше итераций, чтобы, надеюсь, убрать такой шум? - person tripleee; 28.06.2021
comment
@tripleee повторил тест, как опубликованный там, много раз, когда мой стол был относительно тихим, затем выбирал более релевантный результат для публикации там. (Вы пробовали мой сценарий? Я добавил столбец +Numberr, я не буду пытаться там это объяснять;) - person F. Hauri; 28.06.2021
comment
@tripleee Добавлен абзац и результат теста о совместимости с оболочкой - person F. Hauri; 29.06.2021
comment
Попытка использовать csh здесь совершенно неверна, это все равно, что передать код Фортрана Python и удивиться, что он не работает. Оболочки семейства C - это совершенно отдельный язык с другим синтаксисом и функциями, и они несовместимы с оболочками семейства Bourne (за исключением тривиальных сценариев, которые запускают только внешние команды, так же, как вы ожидаете, что пакетный сценарий Windows будет работать в Unix. если действительно просто). Так или иначе, здравомыслящие люди отказались от csh и друзей около 30 лет назад; просто позволь ему умереть. - person tripleee; 29.06.2021
comment
@tripleee Хорошо, csh удален из сообщения и скрипта. - person F. Hauri; 29.06.2021
comment
Ой, извините - небрежное чтение с моей стороны. Извинения; комментарий удален. - person tripleee; 29.06.2021

Просто продолжение @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
person triple_r    schedule 28.02.2014
comment
Печать числа менее полезна, чем установка кода выхода. 'BEGIN { exit(1-(a==a+0)) }' немного сложно понять, но его можно использовать в функции, которая возвращает истину или ложь точно так же, как [, grep -q и т. Д. - person tripleee; 02.08.2017

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
person user2683246    schedule 05.11.2014
comment
Большое спасибо, особенно за man -P 'less +/parameter\/' bash часть. Узнавать что-то новое каждый день. :) - person David; 11.04.2019
comment
@sjas Вы можете легко добавить \- в регулярное выражение, чтобы решить эту проблему. Используйте [0-9\-\.\+] для учета чисел с плавающей запятой и чисел со знаком. - person user2683246; 09.05.2019
comment
@sjas хорошо, моя вина - person user2683246; 09.05.2019
comment
@sjas echo $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no - person user2683246; 09.05.2019

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

isNumber() {
    (( $1 )) 2>/dev/null
}

Согласно странице руководства, это в значительной степени то, что я хочу

Если значение выражения не равно нулю, статус возврата равен 0.

Чтобы предотвратить неприятные сообщения об ошибках для строк, которые «могут быть числами», я игнорирую вывод ошибок

$ (( 2s ))
bash: ((: 2s: value too great for base (error token is "2s")
person Hachi    schedule 04.04.2019
comment
Это неправильно (глючит)! Попробуйте это: foo=1;set -- foo;(( $1 )) 2>/dev/null && echo "'$1' is a number" - person F. Hauri; 25.06.2021

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

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

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

Выход: Valid number.

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

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

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


Объяснение

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

Итак, собрав все вместе, в if тесте мы echo переменную $yournumber и | направляем ее в grep, который с переключателем -q молча совпадает с -E расширенным регулярным выражением '^[0-9]+$' выражение. Статус выхода grep будет 0, если grep успешно найдено совпадение, и 1, если нет. Если удалось сопоставить, мы echo "Valid number.". Если совпадение не получилось, мы echo "Error: not a number.".


Для поплавков или парных

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

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

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

Выход: Valid number.

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

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

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


Для отрицательных

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

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

person Joseph Shih    schedule 20.10.2018
comment
LGTM; ответ как отредактированный имеет мой +1. Единственное, что я бы сделал по-другому на этом этапе, - это просто вопрос мнения, а не правильности (например, использование [-] вместо \- и [.] вместо \. немного более многословно, но это означает, что ваши строки не должны измените, если они используются в контексте, где используются обратные косые черты). - person Charles Duffy; 16.03.2020
comment
Я использовал другой подход с if [[ $yournumber =~ ^[0-9]+([.][0-9]+)?$ ]] ; then в старой системе на основе Ubuntu 14.04, но каким-то образом он перестал работать после обновления до Ubuntu 20.04, ваше первое решение для Test Integer делает то же самое в 20.04. Я не могу сказать, связано ли это с обновлением, или, может быть, мой сценарий был неправильным в первую очередь и - почему-то - все же работал в старой системе. Большое тебе спасибо. - person Geppettvs D'Constanzo; 07.07.2020
comment
@ GeppettvsD'Constanzo, возможно, сценарий использовал #!/bin/sh? Если это так, он должен работать в современной Ubuntu, если вы используете #!/bin/bash shebang, и избегайте запуска скриптов с sh scriptname (который игнорирует shebang и заставляет использовать sh вместо bash). - person Charles Duffy; 11.08.2020
comment
Использование внешнего процесса для чего-то, что встроено в Bash, всегда сомнительно. - person tripleee; 24.06.2021

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

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

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

person Sammitch    schedule 22.08.2013
comment
Это не для пустого var. - person gniourf_gniourf; 14.02.2015
comment
Или для переменных с завершающимися символами новой строки или чем-то вроде $'0\n\n\n1\n\n\n2\n\n\n3\n'. - person gniourf_gniourf; 14.02.2015
comment
Требовать нескольких внешних процессов для чего-то, что оболочка может обрабатывать с использованием чистых встроенных функций, - это просто плохая практика. - person tripleee; 28.06.2021
comment
sed - это тоже трюк с оболочкой - person Sergio Abreu; 03.07.2021

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

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

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

person overflowed    schedule 08.10.2012
comment
либо дубликат, либо, возможно, лучше подходит в качестве комментария к ответу pixelbeat (использование %f, вероятно, в любом случае лучше) - person michael; 19.07.2013
comment
Почему бы вместо проверки предыдущего кода состояния просто не поместить его в сам if? Вот что делает if ... if printf "%g" "$var" &> /dev/null; then ... - person Camilo Martin; 26.06.2014
comment
Здесь есть и другие предостережения. Он проверит пустую строку и строки типа 'a. - person gniourf_gniourf; 14.02.2015
comment
Лучшее решение в моей книге. Я попробовал bc, прежде чем понял, что bc не делает плавающих. Интерпретация пустой строки как числа является незначительной оговоркой (и не интерпретируется как число). - person JPGConnolly; 17.07.2020
comment
@JPGConnly, что ты имеешь в виду, bc не делает плавающих? - person Jdamian; 11.09.2020

Пока не могу комментировать, поэтому я добавлю свой собственный ответ, который является расширением ответа Гленна Джекмана с использованием сопоставления с шаблоном 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)). Мои модульные тесты проверяют только значения, которые являются целыми или плавающими, но не содержат недопустимые входные данные.

person karttu    schedule 24.03.2015

Четкий ответ уже дал @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 или иначе будут экспортировать плавающие числа таким образом), полезным дополнением к этой строке будет следующее:

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] может быть два знака +/-. Я выучил еще несколько правил из работы Аулу и протестировал их на плохих строках, таких как '' '-' '-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
person Aulo    schedule 06.03.2015

[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

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

person D_I    schedule 21.10.2011
comment
Какая минимальная версия bash? Я просто получаю bash: ожидаемый бинарный условный оператор bash: синтаксическая ошибка рядом с неожиданным токеном `= ~ ' - person Paul Hargreaves; 29.11.2011
comment
@PaulHargreaves =~ существовал как минимум еще в bash 3.0. - person Gilles 'SO- stop being evil'; 23.08.2014
comment
@PaulHargreaves у вас, вероятно, возникла проблема с вашим первым операндом, например слишком много кавычек и т.п. - person Joshua Clayton; 05.02.2015
comment
@JoshuaClayton Я спросил о версии, потому что это очень очень старый bash для Solaris 7, который у нас все еще есть, и он не поддерживает = ~ - person Paul Hargreaves; 06.02.2015

Я использую 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 не равно нулю.

person David W.    schedule 27.10.2013
comment
Это довольно плохо. Чтобы сделать его немного лучше, вы должны использовать 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, - person gniourf_gniourf; 14.02.2015
comment
С Bash вам действительно никогда не понадобится expr. Если вы ограничены меньшей оболочкой Борна, такой как POSIX sh, тогда возможно. - person tripleee; 24.06.2021

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

#!/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

Таким образом, isNumber () включает дефисы, запятые и экспоненциальную нотацию и поэтому возвращает ИСТИНА для целых чисел и чисел с плавающей запятой, тогда как, с другой стороны, isFloat () возвращает ЛОЖЬ для целочисленных значений и 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; }
person 3ronco    schedule 08.09.2016
comment
Лично я бы удалил ключевое слово function, поскольку оно не делает ничего полезного. Кроме того, я не уверен в полезности возвращаемых значений. Если не указано иное, функции будут возвращать статус завершения последней команды, поэтому вам не нужно ничего return делать самостоятельно. - person Tom Fenech; 09.09.2016
comment
Приятно, действительно, return сбивают с толку и делают его менее читаемым. Использование function ключевых слов или нет - это скорее вопрос личного вкуса, по крайней мере, я удалил их из одних вкладышей, чтобы сэкономить место. Спасибо. - person 3ronco; 09.09.2016
comment
Не забывайте, что точки с запятой нужны после тестов для однострочных версий. - person Tom Fenech; 09.09.2016
comment
isNumber вернет true для любой строки, содержащей число. - person DrStrangepork; 19.10.2016
comment
@DrStrangepork Действительно, в моем массиве false_values ​​этот случай отсутствует. Я разберусь с этим. Спасибо за подсказку. - person 3ronco; 24.10.2016
comment
Пытался создать комбинированное выражение регулярного выражения для всех трех типов. Это возможно, но это приводит к действительно сложной тарабарщине с регулярными выражениями. Условие мьютекса выглядит действительно простым и читаемым. - person 3ronco; 06.11.2016

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:]] вернет истину. Здесь небольшая ловушка, bash позволяет вам делать что-то вроде "0xAZ00" и при этом считать это цифрой (разве это не из-за какой-то странной причуды компиляторов GCC, которые позволяют вам использовать нотацию 0x для оснований, отличных от 16 ??? )

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

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi
person ultrasawblade    schedule 12.06.2010
comment
[[ $VAR = *[[:digit:]]* ]] вернет истину, если переменная содержит число, но не если оно является целым числом. - person glenn jackman; 26.10.2012
comment
[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric" печатает numeric. Проверено в bash версии 3.2.25(1)-release. - person Jdamian; 08.09.2016
comment
@ultraswadable, ваше решение обнаруживает эти строки, содержащие, по крайней мере, одну цифру, окруженную (или не окруженную) любыми другими символами. Я проголосовал против. - person Jdamian; 08.09.2016
comment
Таким образом, очевидно правильный подход - обратить это вспять и использовать [[ -n $VAR && $VAR != *[^[:digit:]]* ]] - person eschwartz; 23.10.2018
comment
@eschwartz, ваше решение не работает с отрицательными числами - person Angel; 22.03.2019

Я использую 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; }

person Community    schedule 09.03.2015

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

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
person Andrew    schedule 27.07.2015
comment
Для обработки отрицательных чисел потребуется более сложный подход. - person Andrew; 08.05.2017
comment
... Или необязательный положительный знак. - person tripleee; 02.08.2017
comment
@tripleee, я хотел бы увидеть ваш подход, если вы знаете, как это сделать. - person Andrew; 02.08.2017

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

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

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

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

if [ "$var" -ge 0 ] 2> /dev/null; then ..
person user3895088    schedule 30.09.2015
comment
Это не работает только для комплексных чисел (с мнимым компонентом), но также и с числами с плавающей запятой (с нецелочисленным компонентом). - person Charles Duffy; 08.05.2021

Чтобы поймать отрицательные числа:

if [[ $1 == ?(-)+([0-9.]) ]]
    then
    echo number
else
    echo not a number
fi
person user28490    schedule 04.03.2013
comment
Кроме того, для этого сначала необходимо включить расширенное глобальное отображение. Это функция только для Bash, которая по умолчанию отключена. - person tripleee; 02.08.2017
comment
@tripleee расширенная глобализация активируется автоматически при использовании == или! = When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled. gnu.org/software/bash/manual/bashref.html#index-_005b_005b - person Badr Elmers; 13.05.2019
comment
@BadrElmers Спасибо за обновление. Кажется, это новое поведение, которое не соответствует моему Bash 3.2.57 (MacOS Mojave). Я вижу, это работает так, как вы описываете в 4.4. - person tripleee; 13.05.2019

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

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

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

person Idriss Neumann    schedule 27.10.2013
comment
Это очень опасно. Не оценивайте непроверенные арифметические операции в оболочке. Сначала он должен быть подтвержден каким-либо другим способом. - person ormaaj; 08.10.2014

printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

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

person Ane Dijitak    schedule 06.01.2016
comment
Проголосовать за отсутствие объяснений. Как это работает? Он выглядит сложным и хрупким, и неясно, какие именно входные данные он примет. (Например, крайне ли необходимо удалять пробелы? Почему? Он скажет, что число со встроенными пробелами является допустимым числом, что может быть нежелательно.) - person tripleee; 02.08.2017

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

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

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi
person Jerome    schedule 06.05.2018
comment
Не могли бы вы объяснить, почему ваш ответ принципиально отличается от других старых ответов, например, ответа Чарльза Даффи? Что ж, ваш ответ на самом деле неверен, поскольку он подтверждает единственную точку . - person gniourf_gniourf; 07.05.2018
comment
не уверен, что понимаете здесь единственный период ... это один или ожидаемый нулевой период .... Но правильно, ничего принципиально другого, просто обнаружил, что регулярное выражение легче читать. - person Jerome; 09.05.2018
comment
также использование * должно соответствовать большему количеству реальных случаев - person Jerome; 09.05.2018
comment
Дело в том, что вы сопоставляете пустую строку a='' и строку, содержащую только точку a='.', поэтому ваш код немного не работает ... - person gniourf_gniourf; 09.05.2018

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

## ##### 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
person Marnix    schedule 04.05.2009
comment
Почти правильно (вы принимаете пустую строку), но чрезмерно сложный до запутывания. - person Gilles 'SO- stop being evil'; 03.01.2012
comment
Неправильно: вы принимаете -n и т. Д. (Из-за echo) и принимаете переменные с завершающими символами новой строки (из-за $(...)). И, кстати, print не является допустимой командой оболочки. - person gniourf_gniourf; 14.02.2015

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

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

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

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

person ata    schedule 16.10.2010
comment
Читая решение mrucci, оно выглядит почти так же, как мое, но с использованием обычной замены строки вместо стиля sed. Оба используют одни и те же правила для сопоставления с образцом и, AFAIK, взаимозаменяемые решения. - person ata; 17.10.2010
comment
sed - это POSIX, а ваше решение - bash. Оба имеют свое применение - person v010dya; 11.04.2020

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

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
}

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

person WWWIZARDS    schedule 24.12.2010
comment
Это принимает арифметические выражения, такие как 1+1, но отклоняет некоторые положительные целые числа с ведущими 0 (потому что 08 является недопустимой восьмеричной константой). - person Gilles 'SO- stop being evil'; 03.01.2012
comment
У этого есть и другие проблемы: 0 не является числом, и он может быть введен произвольным кодом, попробуйте: isInteger 'a[$(ls)]'. Ой. - person gniourf_gniourf; 14.02.2015
comment
И расширение $((...)) не заключено в кавычки, числовое IFS=123 изменит его. - person ImHere; 20.11.2018

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

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

  • переменная для проверки

    number=12345 or number=-23234 or number=23.167 or number=-345.234

  • проверьте числовой или нечисловой

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

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

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

person Atanu    schedule 14.08.2012

Продолжая ответ Дэвида В. от октября 2013 г., при использовании expr это может быть лучше

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

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

person Jon T    schedule 26.04.2016
comment
expr - зверь, которого трудно приручить. Я не тестировал это решение, но я бы не стал expr в пользу современных встроенных команд оболочки, если только совместимость с устаревшими оболочками начала 1980-х годов не является важным требованием. - person tripleee; 02.08.2017

Синтаксис почти такой, как вы хотите. Просто нужна функция isnumber:

#!/usr/bin/bash

isnumber(){
  num=$1
  if [ -z "${num##*[!0-9]*}" ]; 
    then return 1
  else
    return 0
  fi
}

$(isnumber $1) && VAR=$1 || echo "need a number";
echo "VAR is $VAR"

тестовое задание:

$ ./isnumtest 10
VAR is 10
$ ./isnumtest abc10
need a number
VAR is 
person Daniil Loban    schedule 30.06.2021

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

test $myVariable -eq 0 2>/dev/null
if [ $? -le 1 ]; then echo 'ok'; else echo 'KO'; fi

Если myVariable = 0, код возврата - 0
Если myVariable ›0, код возврата - 1
Если myVariable не целое число, код возврата - 2

person fragadass    schedule 13.07.2021

Принятый ответ не работал у меня во всех случаях BASH 4+, поэтому:

# -- is var an integer? --
# trim leading/trailing whitespace, then check for digits return 0 or 1
# Globals: None
# Arguments: string
# Returns: boolean
# --
is_int() {
    str="$(echo -e "${1}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
    case ${str} in ''|*[!0-9]*) return 1 ;; esac
    return 0
}

Как это использовать ?

Действителен (вернет 0 = истина):

is_int "100" && echo "return 0" || echo "return 1"

Неверно (вернет 1 = ложь):

is_int "100abc" && echo "returned 0" || echo "returned 1"
is_int ""  && echo "returned 0" || echo "returned 1"
is_int "100 100"  && echo "returned 0" || echo "returned 1"
is_int "      "  && echo "returned 0" || echo "returned 1"
is_int $NOT_SET_VAR  && echo "returned 0" || echo "returned 1"
is_int "3.14"   && echo "returned 0" || echo "returned 1"

Выход:

returned 0
returned 1
returned 1
returned 1
returned 1
returned 1
returned 1

обратите внимание, в Bash 1 = false, 0 = true. Я просто распечатываю его там, где более вероятно что-то вроде этого:

if is_int ${total} ; then
    # perform some action 
fi
person Mike Q    schedule 02.06.2019
comment
Могу я попросить вас показать конкретный случай, в котором принятый ответ не работает? - person Charles Duffy; 16.08.2019
comment
... кстати, echo -e будет вводить ошибки переносимости - в некоторых оболочках он выводит -e на выходе (и те оболочки, где это не делает это, нарушают букву Спецификация POSIX для echo; с правильным набором флагов времени выполнения, установленным, чтобы сделать его более строгим совместим, чем обычно, это означает, что bash тоже может печатать -e на выходе при запуске echo -e). - person Charles Duffy; 16.08.2019

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

if [ $(echo "$number" | grep -c '^[0-9]\+$') = 0 ]; then 
    echo "it is a number"
else
    echo "not a number"
fi
person neevek    schedule 26.03.2014
comment
Здесь много проблем: наиболее очевидная из них заключается в том, что вы должны сравнивать с 1, а не с 0 в тестовой инструкции. Другая проблема заключается в том, что если переменная number содержит символы новой строки: number=$'42\nthis is not a number, right?' будет проверяться. - person gniourf_gniourf; 15.02.2015
comment
А? Принятый ответ действительно работает на MacOS. Прокомментируйте, пожалуйста, подробности увиденной проблемы и (в идеале) репродуктор. (Я подозреваю, что, возможно, он запускался под /bin/sh - либо через #!/bin/sh shebang, либо через сценарий, начинающийся с sh scriptname, а не bash). - person Charles Duffy; 24.05.2016
comment
Использование grep -c в подстановке команд и сравнение результата с 0 - распространенный и чрезвычайно запутанный антипаттерн. См. бесполезное использование grep и дополнительную страницу логики кренделя. - person tripleee; 02.08.2017

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

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

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).

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

Я использую заглавную букву, когда результат тестового выражения НЕПРАВИЛЬНЫЙ, тогда как должно быть наоборот!

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

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

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

Идеально!

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

person centurian    schedule 25.09.2016
comment
Нет, пожалуйста, нет !!! это полностью сломано! и он подлежит выполнению произвольным кодом. Попробуйте сами: isInt 'a[$(ls>&3)]' 3>&1. Вы рады, что я использовал ls только как команду (которая выполняется дважды). И ваш код также утверждает, что a[$(ls>&3)] - это число. - person gniourf_gniourf; 25.09.2016
comment
Не говоря уже о том, что он будет утверждать, что любое допустимое имя переменной (которое не установлено, расширяется до числа или пусто, или рекурсивно расширяется до числа или пусто) является числом. Например, unset a; isInt a. - person gniourf_gniourf; 25.09.2016
comment
выполнение произвольного кода кем? вы берете на себя ответственность набрать sudo su, но не выполнить этот или какой-либо сценарий? - person centurian; 27.09.2016
comment
если [[isInt 'a [$ (ls ›& 3)]']]; тогда ... безопасно, так как дает ... ошибки! Вы имеете в виду, что прямое исполнение - это не «a [$ (ls› & 3)] ».... тогда не называйте это прямым !! Вы имеете в виду, что если бы внутри был RM ... Я бы ответил: «Разве вы не знаете, что делает RM?» Я согласен, просто протестируйте его и все в порядке при определенных условиях! Прямо как в жизни! - person centurian; 27.09.2016

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

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

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

person Community    schedule 30.04.2009
comment
В вашем тесте отсутствует 0, не говоря уже об отрицательных числах. - person Gilles 'SO- stop being evil'; 03.01.2012
comment
кроме того, для этого требуется [[ ]] вместо [ ], в противном случае он не работает с нецелочисленными строками. - person naught101; 30.05.2012
comment
[$ number -ge 0 2 ›/ dev / null] && echo isNumber || echo noNumber - person defim; 21.11.2015
comment
-ge больше или равно, поэтому он не должен пропускать 0, но пропускает отрицательные числа. Вышеупомянутое решение также работает лучше, поскольку [[ ]] всегда будет приводить к номеру для ответа @defim, но отлично работает для [ ], поскольку оно генерирует ошибку и оценивает как false. - person Marcus; 07.12.2019
comment
Извините, забыл о негативе выше. Для всех видов использования: [ "$number" -eq "$number" 2>/dev/null ] && echo isNumber || echo noNumber - person defim; 09.12.2019

Ниже приведен сценарий, написанный мной и использованный для интеграции сценария с 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
person Shirish Shukla    schedule 04.09.2011
comment
Это сложно и сломано. Вам нужны двойные кавычки в echo "$a1", иначе символы подстановки в строке расширяются, и результат зависит от того, какие файлы находятся в текущем каталоге (попробуйте isnumsks.sh "*", затем повторите попытку после создания файла с именем 42). Вы смотрите только на первое слово, разделенное пробелами, поэтому 42 psych ошибочно классифицируется как числовое. Все виды ввода, которые не являются числовыми, но имеют допустимый синтаксис bc, могут испортить это, например 2012-01-03. - person Gilles 'SO- stop being evil'; 03.01.2012