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

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

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

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

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

Ответы [ 38 ]

6 голосов
/ 05 ноября 2014
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
6 голосов
/ 12 июня 2010

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
6 голосов
/ 24 марта 2015

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

5 голосов
/ 09 октября 2012

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

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

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

4 голосов
/ 27 октября 2013

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

4 голосов
/ 27 июля 2015

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

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
4 голосов
/ 08 сентября 2016

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

#!/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; }
4 голосов
/ 06 марта 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

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

Чтобы обеспечить более полную поддержку плавающих чисел и научных обозначений (многие программы на 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
2 голосов
/ 20 октября 2018

Этого можно добиться с помощью 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]+$'.

1 голос
/ 07 мая 2018

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

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

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...