/ bin / dash: проверить, является ли $ 1 числом

12

Как лучше всего проверить, является ли $ 1 целым числом в / bin / dash?

В bash я мог бы сделать:

[[ $1 =~ ^([0-9]+)$ ]]

Но это не похоже на POSIX-совместимость и Dash не поддерживает это

Мартин Вегтер
источник

Ответы:

12

Следующее определяет целые числа, положительные или отрицательные, и работает в dashPOSIX:

Опция 1

echo "$1" | grep -Eq '^[+-]?[0-9]+$' && echo "It's an integer"

Вариант 2

case "${1#[+-]}" in
    ''|*[!0-9]*)
        echo "Not an integer" ;;
    *)
        echo "Integer" ;;
esac

Или с небольшим использованием команды :(nop):

! case ${1#[+-]} in *[!0-9]*) :;; ?*) ! :;; esac && echo Integer
John1024
источник
5
Что если строка содержит символы новой строки? foo\n123\nbarне целое число, но пройдет этот тест.
Godlygeek
3
Версия grep, то есть. Версия корпуса выглядит правильно.
Godlygeek
5

Независимо от того dash, bash, ksh, zsh, POSIX sh, или posh( "переопределение Борна оболочки" sh ); caseконструкция является наиболее доступной и надежной:

case $1 in (*[!0-9]*|"") false ;; (*) true ;; esac
Janis
источник
Вы проверяли это под dash? Это работает для меня под, bashно не dash.
John1024
Да, я также проверил это в своей системе dash; чтобы опросить результат я добавил echo $?после команды case.
Янис
Я сделал то же самое (стабильный Debian), но не радости. Для полного примера, который работает для меня под моей чертой, см. Мой Вариант 2 .
John1024
Хм, у меня нет оригинальной оболочки Борна, доступной для тестирования. Документы, которые я проверял на предмет «возможностей [в ksh], а не в оболочке Борна», по крайней мере, не упоминают об этом, поэтому я предполагаю, что это было там. Нет? - Затем опустите ведущие скобки. - OTOH posh(«переопределение оболочки Борна») также не имеет проблем с этим решением.
Янис
1
@mikeserv; Есть несколько причин, почему я использую всегда совпадающие скобки в case; Одна из причин - это ошибка, которую вы описываете, другая - в редакторах, которые имеют функции, основанные на сопоставлении скобок (vim), они дают гораздо лучшую поддержку, и, что не менее важно, я считаю, что лично лучше, чтобы они соответствовали. - WRT posh- это POSIX; ну, цитата из справочной страницы, которую я дал, предложила кое-что еще, но я не могу полагаться на такие неофициальные заявления в любом случае. Старая оболочка Bourne уже не так важна, как сейчас, в эпоху POSIX.
Janis
1

Вы можете использовать -eqтест для строки с самим собой:

$ dash -c 'a="a"; if [ "$a" -eq "$a" ] ; then echo number; else echo not a number; fi' 
dash: 1: [: Illegal number: a
not a number
$ dash -c 'a="0xa"; if [ "$a" -eq "$a" ] ; then echo number; else echo not a number; fi'
dash: 1: [: Illegal number: 0xa
not a number
$ dash -c 'a="-1"; if [ "$a" -eq "$a" ] ; then echo number; else echo not a number; fi'
number

Если сообщение об ошибке является проблемой, перенаправьте вывод ошибки на /dev/null:

$ dash -c 'a="0xa"; [ "$a" -eq "$a" ] 2>/dev/null|| echo no'
no
Мур
источник
1
Было бы также сказать, что " 023 "это число. Обратите внимание, что он работает с дефисом, но не со всеми другими оболочками POSIX, так как поведение не определено, если операнды представляют собой целые числа после запятой. Например, с помощью ksh можно сказать, что SHLVLили 1+1это число.
Стефан Шазелас
0

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

check_if_number()
{
    if [ "$1" = "$((${1}))" ] 2>/dev/null; then
        echo "Number!"
    else
        echo "not a number"
    fi
}

Это также будет принимать отрицательные числа - если вы действительно хотите исключить их, добавьте дополнительную проверку для $((${1} >= 0)).

godlygeek
источник
1
Тире не имеет[[
Гленн Джекман
@glennjackman Упс. Это имеет $(( ... ))? Если это так, мой ответ все еще должен быть материально правильным, мне просто нужно добавить дополнительные цитаты.
Godlygeek
это твой ответ: пойди узнай.
Гленн Джекман
Похоже, что так и есть, поэтому моя обновленная версия должна помочь. У меня нет ни малейшего желания это проверять, поэтому дайте мне знать, если я что-то пропустил.
Godlygeek
Я попробовал: check_if_number 1.2и функция вернулась: dash: 3: arithmetic expression: expecting EOF: "1.2"
John1024
0

Возможно с expr?

if expr match "$1" '^\([0-9]\+\)$' > /dev/null; then
  echo "integer"
else 
  echo "non-integer"
fi
steeldriver
источник
ни POSIX, matchни \+Было бы также сказать, 0 не число. Вы хотитеexpr "x$1" : 'x[0-9]\{1,\}$'
Стефан Шазелас
0

В системе POSIX вы можете использовать expr :

$ a=a
$ expr "$a" - 0 >/dev/null 2>&1
$ [ "$?" -lt 2 ] && echo Integer || echo Not Integer
cuonglm
источник
Он скажет, что 0 не является целым числом (а, скажем, -12 - это то, что решение bash OP отклонило бы). Некоторые exprреализации скажут, что 9999999999999999999 не является целым числом. POSIX не дает никаких гарантий, что это будет работать. На практике, по крайней мере, в системе GNU будет сказано, что «длина» - это целое число.
Стефан Шазелас
В моем тесте это работает по крайней мере в Debian и OSX. Он говорит целое число для 0. Posix гарантирует, что $ a должно быть допустимым выражением (целое число) для expr do arithmetic.
cuonglm
О да, извините, я пропустил ваш тест на $? <2. Тем не менее expr 9999999999999999999 + 0дает мне статус на 3 выхода и expr -12 + 0и expr length + 0дать мне статус в 0 выездном с GNU выража ( + stringсилами , stringкоторые будут рассматриваться как строка с GNU expr. expr "$a" - 0Будет работать лучше).
Стефан Шазелас
@ StéphaneChazelas: О да, обновил мой ответ. Я думаю, что -12является допустимым целым числом и 9999999999999999999дал переполнение.
cuonglm
0

Вот простая функция, использующая тот же метод, что и ответ muru :

IsInteger()      # usage: IsInteger string
{               #  returns: flag
        [ "$1" -eq "$1" ] 2> /dev/null
}

Пример:

p= n=2a3; IsInteger $n || p="n't" ; printf "'%s' is%s an integer\n" "$n" "$p"
p= n=23;  IsInteger $n || p="n't" ; printf "'%s' is%s an integer\n" "$n" "$p"

Выход:

'2a3' isn't an integer
'23' is an integer
АРУ
источник