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

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

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

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

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

41 ответ

Решение

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

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

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

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

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

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

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

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

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

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

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

"$var" -eq "$var"

как в:

#!/bin/bash

var=a

if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; then
  echo number
else
  echo not a number
fi

Вы можете также проверить на $? код возврата операции, которая является более явной:

[ -n "$var" ] && ["$var" -eq "$var"] 2>/dev/null
if [ $? -ne 0 ]; then
   echo $var is not number
fi

Перенаправление стандартной ошибки предназначено для того, чтобы скрыть сообщение "ожидается целочисленное выражение", которое распечатывает bash, если у нас нет числа.

ПРЕДОСТЕРЕЖЕНИЯ (благодаря комментариям ниже):

  • Числа с десятичными точками не идентифицируются как действительные "числа"
  • С помощью [[ ]] вместо [ ] всегда буду оценивать true
  • Большинство небашевых оболочек всегда будет оценивать это выражение как true
  • Поведение в Bash недокументировано и поэтому может меняться без предупреждения
  • Если значение включает пробелы после числа (например, "1 a"), возникает ошибка, например bash: [[: 1 a: syntax error in expression (error token is "a")
  • Если значение совпадает с var-name (например, i = "i"), выдает ошибку, например bash: [[: i: expression recursion level exceeded (error token is "i")

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

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

Баш регулярное выражение против Баша расширения параметра

Поскольку ответ Чарльза Даффи работает, но использует только регулярное выражение bash, и я знаю, что это будет медленно, я хотел бы показать другой способ, используя только расширение параметра bash:

Только для положительных целых чисел:

is_num() { [ "$1" ] && [ -z "${1//[0-9]}" ] ;}

Для целых чисел:

is_num() { 
    local chk=${1#[+-]};
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}

Затем для плавания:

is_num() { 
    local chk=${1#[+-]};
    chk=${chk/.}
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}

Для соответствия первоначальному запросу:

set -- "foo bar"
is_num "$1" && VAR=$1 || echo "need a number"
need a number

set -- "+3.141592"
is_num "$1" && VAR=$1 || echo "need a number"

echo $VAR
+3.141592

А теперь небольшое сравнение:

Есть такая же проверка, основанная на ответе Чарльза Даффи:

cdIs_num() { 
    local re='^[+-]?[0-9]+([.][0-9]+)?$';
    [[ $1 =~ $re ]]
}

Некоторые тесты:

if is_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if is_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number

Хорошо, это нормально. А теперь сколько времени займет все это (на моем raspberry pi):

time for i in {1..1000};do is_num +3.14159265;done
real    0m2.476s
user    0m1.235s
sys     0m0.000s

Затем с помощью регулярных выражений:

time for i in {1..1000};do cdIs_num +3.14159265;done
real    0m4.363s
user    0m2.168s
sys     0m0.000s

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

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

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

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

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

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

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

Чтобы проверить прямо в командной строке:

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

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

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

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

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

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

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

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

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`

Просто продолжение до @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
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

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

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

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

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

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

$ (( 2s ))
bash: ((: 2s: value too great for base (error token is "2s")

Это может быть достигнуто с помощью 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 а также | передать это 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]+$',

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

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

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

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

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

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

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

Четкий ответ уже дали @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] может быть два знака +/-. Я узнал еще несколько правил из работы Аулу и проверил на наличие плохих строк, таких как '' '-' '-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
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

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

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

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

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi

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

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

#!/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() включает в себя тире, запятые и экспоненциальную запись, и поэтому возвращает TRUE для целых чисел и чисел с плавающей запятой, тогда как 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; }

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

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

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

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

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

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

Вы можете использовать "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+, как некоторые ответы в этой теме.

Синтаксис почти такой, как вы хотите. Просто нужна функция 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 

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

if [[ $1 == ?(-)+([0-9.]) ]]
    then
    echo number
else
    echo not a number
fi

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

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

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi

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

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 и регулярным выражением Даффи - они кажутся наиболее надежными в рамках bash...

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

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

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

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

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

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

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

Другие вопросы по тегам