Проверьте, является ли строка действительным целым числом

Я пытаюсь сделать что-то достаточно распространенное: введите пользовательский ввод в оболочку script. Если пользователь предоставил действительное целое число, script делает одно, а если оно недействительно, оно делает что-то еще. Проблема в том, что я не нашел простой (и достаточно элегантный) способ сделать это - я не хочу выделять его char на char.

Я знаю, что это должно быть легко, но я не знаю, как это сделать. Я мог бы сделать это на десятках языков, но не BASH!

В моих исследованиях я нашел это:

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

И есть ответ, в котором говорится о регулярном выражении, но насколько я знаю, функция, доступная в C (среди прочего). Тем не менее, у этого был отличный ответ, поэтому я попробовал его с grep, но grep не знал, что с ним делать. Я попытался -P, который на моей коробке означает рассматривать его как PREL regexp-nada. Dash E (-E) тоже не работает. И не делал -F.

Просто, чтобы быть ясным, я пытаюсь что-то вроде этого, ища какой-либо результат - оттуда я взломаю script, чтобы воспользоваться тем, что получаю. (IOW, я ожидал, что несоответствующий вход ничего не возвращает, пока действительная строка будет повторяться.)

snafu=$(echo "$2" | grep -E "/^[-+]?(?:\.[0-9]+|(?:0|[1-9][0-9]*)(?:\.[0-9]*)?)$/")
if [ -z "$snafu" ] ;
then
   echo "Not an integer - nothing back from the grep"
else
   echo "Integer."
fi

Кто-нибудь, пожалуйста, проиллюстрирует, как это легче всего сделать?

Честно говоря, это краткосрочный тест TEST, на мой взгляд. Он должен иметь такой флаг

if [ -I "string" ] ;
then
   echo "String is a valid integer."
else
   echo "String is not a valid integer."
fi

Ответ 1

[[ $var =~ ^-?[0-9]+$ ]]
  • ^ указывает начало шаблона ввода
  • - является литералом "-"
  • ? означает "0 или 1 предыдущего (-)"
  • + означает "1 или более предыдущих ([0-9])"
  • $ указывает конец шаблона ввода

Итак, регулярное выражение соответствует необязательному - (для отрицательных чисел), за которым следует одна или несколько десятичных цифр.

Ссылки

Ответ 2

Ничего себе... здесь так много хороших решений! Из всех вышеприведенных решений я согласен с @nortally, что с помощью -eq один вкладыш является самым крутым.

Я запускаю GNU bash, версия 4.1.5 (Debian). Я также проверил это на ksh (SunSO 5.10).

Вот моя версия проверки, если $1 является целым числом или нет:

if [ "$1" -eq "$1" ] 2>/dev/null
then
    echo "$1 is an integer !!"
else
    echo "ERROR: first paramter must be an integer."
    echo $USAGE
    exit 1
fi

Это также заказывает номер -ve, который в некоторых из вышеперечисленных решений будет иметь отрицательный результат с ошибкой, и он будет иметь префикс "+", например. + 30, который, очевидно, является целым числом.

Результаты:

$ int_check.sh 123
123 is an integer !!

$ int_check.sh 123+
ERROR: first paramter must be an integer.

$ int_check.sh -123
-123 is an integer !!

$ int_check.sh +30
+30 is an integer !!

$ int_check.sh -123c
ERROR: first paramter must be an integer.

$ int_check.sh 123c
ERROR: first paramter must be an integer.

$ int_check.sh c123
ERROR: first paramter must be an integer.

Решение, предоставленное Игнасио Васкес-Абрамом, также было очень аккуратным (если вам нравится регулярное выражение) после его объяснения. Однако он не обрабатывает положительные числа с префиксом +, но его можно легко зафиксировать следующим образом:

[[ $var =~ ^[-+]?[0-9]+$ ]]

Ответ 3

Latecomer на вечеринку здесь. Я очень удивлен, что ни один из ответов не упоминает простейшее, быстрое и портативное решение; case.

case ${variable#[-+]} in
  *[!0-9]* | '') echo Not a number ;;
  * ) echo Valid number ;;
esac

Обрезка любого знака перед сравнением кажется немного взломанным, но это делает выражение для оператора case намного проще.

Ответ 4

Для переносимости до Bash 3.1 (когда был введен тест =~), используйте expr.

if expr "$string" : '-\?[0-9]\+$' >/dev/null
then
  echo "String is a valid integer."
else
  echo "String is not a valid integer."
fi

expr STRING : REGEX ищет REGEX, привязанный к началу STRING, повторяя первую группу (или длину совпадения, если она отсутствует) и возвращает успех/сбой. Это старый синтаксис regex, следовательно, избыток \. -\? означает "возможно -", [0-9]\+ означает "одна или несколько цифр", а $ означает "конец строки".

Bash также поддерживает расширенные шары, хотя я не помню, с какой версии и дальше.

shopt -s extglob
case "$string" of
    @(-|)[0-9]*([0-9]))
        echo "String is a valid integer." ;;
    *)
        echo "String is not a valid integer." ;;
esac

# equivalently, [[ $string = @(-|)[0-9]*([0-9])) ]]

@(-|) означает "- или ничего", [0-9] означает "цифра", а *([0-9]) означает "ноль или более цифр".

Ответ 5

Мне нравится решение, использующее тест -eq, потому что это в основном однострочный.

Мое собственное решение состояло в том, чтобы использовать расширение параметра, чтобы выбросить все цифры и посмотреть, осталось ли что-то. (Я все еще использую 3.0, раньше не использовал [[ или expr, но рад их встретить.)

if [ "${INPUT_STRING//[0-9]}" = "" ]; then
  # yes, natural number
else
  # no, has non-numeral chars
fi

Ответ 6

Здесь еще один возьмите его (только используя встроенную команду test и его код возврата):

function is_int() { return $(test "[email protected]" -eq "[email protected]" > /dev/null 2>&1); } 

input="-123"

if $(is_int "${input}");
then
   echo "Input: ${input}"
   echo "Integer: $[${input}]"
else
   echo "Not an integer: ${input}"
fi

Ответ 7

Вы можете разделить цифры и выполнить сравнение. Вот демо script:

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09"
do
    match=${num//[^[:digit:]]}    # strip non-digits
    match=${match#0*}             # strip leading zeros
    echo -en "$num\t$match\t"
    case $num in
        $match|-$match)    echo "Integer";;
                     *)    echo "Not integer";;
    esac
done

Вот как выглядит тестовый вывод:

44      44      Integer
-44     44      Integer
44-     44      Not integer
4-4     44      Not integer
a4      4       Not integer
4a      4       Not integer
.4      4       Not integer
4.4     44      Not integer
-4.4    44      Not integer
09      9       Not integer

Ответ 8

Для меня самым простым решением было использование переменной внутри выражения (()), так как:

if ((VAR > 0))
then
  echo "$VAR is a positive integer."
fi

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

Как указано в комментариях, это может заставить вас подвергнуть атаке на выполнение кода. Оператор (( )) оценивает VAR, как указано в разделе Arithmetic Evaluation справочная страница bash (1). Поэтому вы не должны использовать эту технику, если источник содержимого VAR не определен (и вы, конечно же, не должны использовать любую другую форму расширения переменных).

Ответ 9

или с sed:

   test -z $(echo "2000" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # integer

   test -z $(echo "ab12" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # no integer

Ответ 10

Добавление к ответу Игнасио Васкес-Абрамса. Это позволит знаку + предшествовать целому числу, и это позволит любое количество нулей в виде десятичных точек. Например, это позволит считать +45.00000000 целым числом.
Однако 1 доллар должен быть отформатирован, чтобы содержать десятичную точку. 45 здесь не считается целым числом, но 45,0 является.

if [[ $1 =~ ^-?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
elif [[ $1 =~ ^\+?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
else
    echo "no, this is not an integer"
fi

Ответ 11

Для смеха я чуть-чуть быстро разработал набор функций для этого (is_string, is_int, is_float, является альфа-строкой или другим), но есть более эффективные (меньше кода) способы сделать это:

#!/bin/bash

function strindex() {
    x="${1%%$2*}"
    if [[ "$x" = "$1" ]] ;then
        true
    else
        if [ "${#x}" -gt 0 ] ;then
            false
        else
            true
        fi
    fi
}

function is_int() {
    if is_empty "${1}" ;then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^0-9]*//g')
    if [[ $tmp == "${1}" ]] || [[ "-${tmp}" == "${1}" ]] ; then
        #echo "INT (${1}) tmp=$tmp"
        true
    else
        #echo "NOT INT (${1}) tmp=$tmp"
        false
    fi
}

function is_float() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if ! strindex "${1}" "-" ; then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^a-z. ]*//g')
    if [[ $tmp =~ "." ]] ; then
        #echo "FLOAT  (${1}) tmp=$tmp"
        true
    else
        #echo "NOT FLOAT  (${1}) tmp=$tmp"
        false
    fi
}

function is_strict_string() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if [[ "${1}" =~ ^[A-Za-z]+$ ]]; then
        #echo "STRICT STRING (${1})"
        true
    else
        #echo "NOT STRICT STRING (${1})"
        false
    fi
}

function is_string() {
    if is_empty "${1}" || is_int "${1}" || is_float "${1}" || is_strict_string "${1}" ;then
        false
        return
    fi
    if [ ! -z "${1}" ] ;then
        true
        return
    fi
    false
}
function is_empty() {
    if [ -z "${1// }" ] ;then
        true
    else
        false
    fi
}

Запустите некоторые тесты здесь, я определил, что -44 - это int, но 44 - нет и т.д.:

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09" "hello" "h3llo!" "!!" " " "" ; do
    if is_int "$num" ;then
        echo "INT = $num"

    elif is_float "$num" ;then
        echo "FLOAT = $num"

    elif is_string "$num" ; then
        echo "STRING = $num"

    elif is_strict_string "$num" ; then
        echo "STRICT STRING = $num"
    else
        echo "OTHER = $num"
    fi
done

Вывод:

INT = 44
INT = -44
STRING = 44-
STRING = 4-4
STRING = a4
STRING = 4a
FLOAT = .4
FLOAT = 4.4
FLOAT = -4.4
INT = 09
STRICT STRING = hello
STRING = h3llo!
STRING = !!
OTHER =  
OTHER = 

ПРИМЕЧАНИЕ. Leading 0 может вывести что-то еще при добавлении чисел, таких как восьмеричные, поэтому было бы лучше снять их, если вы намерены обрабатывать "09" как int (что я делаю) (например, expr 09 + 0 или strip с sed)