Jak sprawdzić, czy zmienna jest liczbą w Bash?

598

Po prostu nie mogę się dowiedzieć, jak upewnić się, że argument przekazany do mojego skryptu jest liczbą, czy nie.

Chcę tylko coś takiego:

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

Jakaś pomoc?

Flávio Amieiro
źródło
17
Nawiasem mówiąc - zastosowane test && echo "foo" && exit 0 || echo "bar" && exit 1podejście może mieć pewne niezamierzone skutki uboczne - jeśli echo się nie powiedzie (być może sygnał wyjściowy jest do zamkniętego FD), exit 0zostanie ono pominięte, a kod spróbuje to zrobić echo "bar". Jeśli to też zawiedzie, &&warunek się nie powiedzie i nawet się nie wykona exit 1! Używanie rzeczywistych ifinstrukcji zamiast &&/ ||jest mniej podatne na nieoczekiwane skutki uboczne.
Charles Duffy,
@CharlesDuffy Jest to rodzaj naprawdę sprytnego myślenia, do którego większość ludzi dociera tylko wtedy, gdy musi wyśledzić włochate robale ...! Nigdy nie sądziłem, że echo zwróci błąd.
Camilo Martin
6
Trochę za późno na imprezę, ale wiem o niebezpieczeństwach, o których pisał Charles, ponieważ musiałem przez nie przejść. Oto więc w 100% głupia (i dobrze czytelna) linia dla ciebie: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }Nawiasy klamrowe wskazują, że NIE należy wykonywać rzeczy w podpowłoce (co zdecydowanie byłoby tak z ()nawiasami). Uwaga: nigdy nie przegap ostatniego średnika . W przeciwnym razie możesz spowodować bashwydrukowanie najbrzydszych (i najbardziej bezcelowych) komunikatów o błędach ...
syntaxerror
5
Nie działa w Ubuntu, chyba że nie usuniesz cudzysłowów. Tak powinno być[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Treviño,
4
Musisz sprecyzować, co rozumiesz przez „liczbę” . Liczba całkowita? Numer stały? Notacja naukowa („e”)? Czy istnieje wymagany zakres (np. 64-bitowa wartość bez znaku), czy dopuszczasz dowolną liczbę, którą można zapisać?
Toby Speight

Odpowiedzi:

802

Jednym z podejść jest użycie wyrażenia regularnego, takiego jak:

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

Jeśli wartość niekoniecznie jest liczbą całkowitą, rozważ odpowiednią zmianę wyrażenia regularnego; na przykład:

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

... lub, aby obsługiwać liczby ze znakiem:

^[+-]?[0-9]+([.][0-9]+)?$
Charles Duffy
źródło
7
+1 dla tego podejścia, ale uważaj na dziesiętne, wykonując ten test, na przykład z błędem „1.0” lub „1,0” drukuje: „Brak liczby”.
sourcerebels
15
Znajduję „” exec> & 2; echo ... '' raczej głupie. Po prostu „echo ...> i 2”
lhunath
5
@ Ben czy naprawdę chcesz obsłużyć więcej niż jeden znak minus? Zrobiłbym to ^-?, ^-*chyba że faktycznie wykonujesz pracę, aby poprawnie obsłużyć wiele inwersji.
Charles Duffy
4
@SandraSchlichting Sprawia, że ​​wszystkie przyszłe dane wyjściowe trafiają do stderr. Naprawdę nie ma tu sensu, gdzie jest tylko jedno echo, ale mam w zwyczaju nawyk, gdy komunikaty o błędach obejmują wiele wierszy.
Charles Duffy
29
Nie jestem pewien, dlaczego wyrażenie regularne musi być zapisane w zmiennej, ale jeśli jest to ze względu na kompatybilność, nie uważam tego za konieczne. Można po prostu zastosować wyrażenie bezpośrednio: [[ $yournumber =~ ^[0-9]+$ ]].
konsolebox
282

Bez baszizmów (działa nawet w Systemie V sh),

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

To odrzuca puste ciągi i ciągi zawierające cyfry, akceptując wszystko inne.

Liczby ujemne lub zmiennoprzecinkowe wymagają dodatkowej pracy. Pomysł polega na wykluczeniu -/ .w pierwszym „złym” wzorcu i dodaniu kolejnych „złych” wzorców zawierających ich niewłaściwe użycie ( ?*-*/ *.*.*)

Jilles
źródło
20
+1 - jest to idiomatyczny, przenośny sposób powrotu do oryginalnej powłoki Bourne'a i ma wbudowaną obsługę symboli wieloznacznych w stylu globalnym. Jeśli pochodzisz z innego języka programowania, wygląda niesamowicie, ale jest o wiele bardziej elegancki niż radzenie sobie z kruchością różnych problemów z cytowaniem i niekończącymi się problemami z kompatybilnością wsteczną / boczną zif test ...
tripleee
6
Możesz zmienić pierwszą linię na ${string#-}(która nie działa w starych powłokach Bourne'a, ale działa w dowolnej powłoce POSIX), aby zaakceptować liczby całkowite ujemne.
Gilles „SO- przestań być zły”
4
Łatwo jest również rozszerzyć na liczby zmiennoprzecinkowe - wystarczy dodać '.' | *.*.*do niedozwolonych wzorów i dodać kropkę do dozwolonych znaków. Podobnie możesz wcześniej zezwolić na znak opcjonalny, chociaż wtedy wolałbym case ${string#[-+]}po prostu zignorować znak.
tripleee
Zobacz to do obsługi podpisanych liczb całkowitych: stackoverflow.com/a/18620446/2013911
Niklas Peter
2
@Dor Cudzysłowy nie są potrzebne, ponieważ polecenie case i tak nie wykonuje podziału słowa i generowania nazwy ścieżki dla tego słowa. (Jednak rozszerzenia wzorców liter mogą wymagać cytowania, ponieważ określa, czy znaki pasujące do wzorca są dosłowne, czy specjalne.)
jilles
193

Poniższego rozwiązania można również użyć w podstawowych powłokach, takich jak Bourne, bez potrzeby używania wyrażeń regularnych. Zasadniczo wszelkie operacje oceny wartości liczbowych z użyciem liczb innych niż powodują błąd, który domyślnie będzie uważany za fałszywy w powłoce:

"$var" -eq "$var"

jak w:

#!/bin/bash

var=a

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

Możesz także przetestować za $? kod powrotu operacji, który jest bardziej wyraźny:

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

Przekierowanie standardowego błędu ma na celu ukrycie komunikatu „oczekiwane wyrażenie liczby całkowitej”, który bash wypisuje na wypadek, gdybyśmy nie mieli numeru.

PRZESTROGI (dzięki komentarzom poniżej):

  • Liczby z kropkami dziesiętnymi nie są identyfikowane jako prawidłowe „liczby”
  • Używanie [[ ]]zamiast [ ]zawsze zawsze będzie oznaczaćtrue
  • Większość powłok innych niż Bash zawsze ocenia to wyrażenie jako true
  • Zachowanie w Bash jest nieudokumentowane i dlatego może ulec zmianie bez ostrzeżenia
  • Jeśli wartość zawiera spacje po liczbie (np. „1 a”) powoduje błąd, jak bash: [[: 1 a: syntax error in expression (error token is "a")
  • Jeśli wartość jest taka sama jak nazwa-zmiennej (np. I = „i”), powoduje błąd, np bash: [[: i: expression recursion level exceeded (error token is "i")
Alberto Zaccagni
źródło
8
Chciałbym jeszcze polecić to (ale ze zmiennymi cytowanych w celu umożliwienia pustych strunach), ponieważ wynik jest gwarancją użyteczny jako liczba w bash, nie wiem co.
l0b0,
21
Uważaj, aby używać pojedynczych nawiasów; [[ a -eq a ]]
przyjmuje
3
Bardzo dobrze! Zauważ, że działa to tylko dla liczby całkowitej, a nie dla dowolnej liczby. Musiałem sprawdzić pojedynczy argument, który musi być liczbą całkowitą, więc zadziałało to dobrze:if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
haridsv
6
Zdecydowanie odradzałbym tę metodę ze względu na niemałą liczbę powłok, których [wbudowane narzędzie oceni argumenty jako arytmetyczne. Dotyczy to zarówno ksh93, jak i mksh. Ponadto, ponieważ obie te tablice obsługują, istnieje łatwa możliwość wstrzyknięcia kodu. Zamiast tego użyj dopasowania wzorca.
ormaaj,
3
@AlbertoZaccagni, w bieżących wersjach bash te wartości są interpretowane za pomocą reguł kontekstu numerycznego tylko dla, [[ ]]ale nie dla [ ]. To powiedziawszy, takie zachowanie nie jest określone zarówno przez standard POSIX, jak testi we własnej dokumentacji bash; przyszłe wersje bash mogłyby modyfikować zachowanie tak, aby pasowały do ​​ksh bez naruszania jakichkolwiek udokumentowanych obietnic behawioralnych, więc nie można zagwarantować, że poleganie na jego bieżącym zachowaniu będzie bezpieczne.
Charles Duffy
43

Sprawdza, czy liczba jest nieujemną liczbą całkowitą i jest niezależna od powłoki (tj. Bez bashism) i wykorzystuje tylko wbudowane powłoki:

BŁĘDNY.

Ponieważ ta pierwsza odpowiedź (poniżej) pozwala na liczby całkowite ze znakami, o ile pierwsze nie są pierwsze w zmiennej.

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

PRAWIDŁOWO .

Jak skomentował i zasugerował w swojej odpowiedzi Jilles, jest to właściwy sposób, aby to zrobić za pomocą wzorców powłok.

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";
mrucci
źródło
5
To nie działa poprawnie, akceptuje dowolny ciąg zaczynający się od cyfry. Zauważ, że WORD w $ {VAR ## WORD} i tym podobne to wzór powłoki, a nie wyrażenie regularne.
jilles
2
Czy możesz przetłumaczyć to wyrażenie na angielski? Naprawdę chcę go używać, ale nie rozumiem go wystarczająco, aby mu zaufać, nawet po przejrzeniu strony podręcznika użytkownika bash.
CivFan
2
*[!0-9]*to wzorzec, który pasuje do wszystkich ciągów o co najmniej 1 znaku innym niż cyfrowy. ${num##*[!0-9]*}to „rozszerzenie parametru”, w którym bierzemy zawartość numzmiennej i usuwamy najdłuższy ciąg pasujący do wzorca. Jeśli wynik interpretacji parametru nie jest pusty ( ! [ -z ${...} ]), to jest to liczba, ponieważ nie zawiera on żadnego znaku innego niż cyfra.
mrucci
Niestety kończy się to niepowodzeniem, jeśli w argumencie są jakieś cyfry, nawet jeśli nie jest to poprawna liczba. Na przykład „egzamin1ple” lub „a2b”.
studgeek
Oba zawodzą z122s :-(
Hastur
40

Nikt nie sugerował rozszerzonego dopasowania wzoru bash :

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"
Glenn Jackman
źródło
5
Glenn, shopt -s extglobusuwam z twojego postu (który głosowałem, jest to jedna z moich ulubionych odpowiedzi tutaj), ponieważ w Konstrukcjach warunkowych możesz przeczytać: Gdy używane są operatory ==i !=, ciąg po prawej stronie operatora jest uważany za wzorzec i dopasowany zgodnie z zasadami opisanymi poniżej w Dopasowywaniu wzorców , tak jakby extglobopcja powłoki była włączona. Mam nadzieję, że nie masz nic przeciwko!
gniourf_gniourf
W takich kontekstach nie musisz shopt extglob... dobrze wiedzieć!
gniourf_gniourf
Działa dobrze dla prostych liczb całkowitych.
2
@Jdamian: masz rację, został dodany w Bash 4.1 (który został wydany pod koniec 2009 roku… Bash 3.2 został wydany w 2006 roku… To jest teraz antyczne oprogramowanie, przepraszam za tych, którzy utknęli w przeszłości). Można również argumentować, że rozszerzenia zostały wprowadzone w wersji 2.02 (wydanej w 1998 r.) I nie działają w wersjach <2.02… Teraz twój komentarz będzie ostrzeżeniem dotyczącym starszych wersji.
gniourf_gniourf
1
Zmienne wewnątrz [[...]]nie podlegają podziałowi słów ani rozszerzaniu globu.
glenn jackman
26

Jestem zaskoczony rozwiązaniami bezpośrednio analizującymi formaty liczb w powłoce. shell nie nadaje się do tego dobrze, ponieważ jest DSL do kontrolowania plików i procesów. Trochę niżej jest dużo parserów liczb, na przykład:

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
}

Zmień '% f' na dowolny wymagany format.

pixelbeat
źródło
4
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
Gilles Quenot
4
@sputnick twoja wersja łamie nieodłączną (i przydatną) semantykę wartości zwracanych przez funkcję oryginalną. Zamiast tego po prostu pozostaw funkcję taką, jaka jest i użyj jej:isnumber 23 && echo "this is a number" || echo "not a number"
michael
4
Czy to też nie powinno 2>/dev/null, aby isnumber "foo"nie zanieczyszczało stderr?
gioele
4
Nazywanie współczesnych powłok, takich jak bash, „DSL do kontrolowania plików i procesów” jest ignorowaniem tego, że są one używane o wiele więcej - niektóre dystrybucje zbudowały na nim całe menedżery pakietów i interfejsy sieciowe (choć może to brzydkie). Pliki wsadowe pasują jednak do Twojego opisu, ponieważ nawet ustawienie zmiennej jest trudne.
Camilo Martin
7
Zabawne, że próbujesz być mądry, kopiując niektóre idiomy z innych języków. Niestety nie działa to w powłokach. Powłoki są bardzo wyjątkowe i bez solidnej wiedzy na ich temat możesz pisać złamany kod. Twój kod jest uszkodzony: isnumber "'a"zwróci wartość true. Jest to udokumentowane w specyfikacji POSIX, w której przeczytasz: Jeśli wiodącym znakiem jest pojedynczy cudzysłów lub podwójny cudzysłów, wartością powinna być wartość liczbowa w podstawowym zestawie znaków znaku następującego po pojedynczym cudzysłowie lub podwójnym cudzysłowie .
gniourf_gniourf
16

Patrzyłem na odpowiedzi i ... zdałem sobie sprawę, że nikt nie myślał o liczbach FLOAT (z kropką)!

Świetnie jest też używać grep.
-E oznacza rozszerzone wyrażenie regularne
-q oznacza ciche (nie echo)
-qE jest kombinacją obu.

Aby przetestować bezpośrednio w wierszu polecenia:

$ 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

Używanie w skrypcie 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

Aby dopasować liczby całkowite JUST, użyj tego:

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`
Sergio Abreu
źródło
Rozwiązania wykorzystujące awk by triple_r i tripleee działają z pływakami.
Ken Jackson
Dzięki za to i bardzo dobry punkt! Ponieważ pytanie polega na tym, jak sprawdzić, czy jest to liczba, a nie tylko liczba całkowita.
Tanasis
Ja też dziękuję Tanasis! Pomóżmy sobie zawsze.
Sergio Abreu
12

Tylko kontynuacja @mary. Ale ponieważ nie mam wystarczającej liczby przedstawicieli, nie mogłem tego opublikować jako komentarza do tego postu. W każdym razie oto, czego użyłem:

isnum() { awk -v a="$1" 'BEGIN {print (a == a + 0)}'; }

Funkcja zwróci „1”, jeśli argument jest liczbą, w przeciwnym razie zwróci „0”. Działa to zarówno dla liczb całkowitych, jak i liczb zmiennoprzecinkowych. Użycie jest takie jak:

n=-2.05e+07
res=`isnum "$n"`
if [ "$res" == "1" ]; then
     echo "$n is a number"
else
     echo "$n is not a number"
fi
triple_r
źródło
4
Drukowanie numeru jest mniej przydatne niż ustawienie kodu wyjścia. 'BEGIN { exit(1-(a==a+0)) }'jest nieco trudne do grok, ale mogą być wykorzystywane w funkcji, która zwraca prawdę lub fałsz, tak jak [, grep -qitp
tripleee
9
test -z "${i//[0-9]}" && echo digits || echo no no no

${i//[0-9]}zastępuje dowolną cyfrę wartości $ipustym łańcuchem, patrz man -P 'less +/parameter\/' bash. -zsprawdza, czy wynikowy łańcuch ma zerową długość.

jeśli chcesz również wykluczyć przypadek, gdy $ijest pusty, możesz użyć jednej z tych konstrukcji:

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
użytkownik2683246
źródło
Kciuki do góry, szczególnie za tę man -P 'less +/parameter\/' bashczęść. Uczenie się czegoś nowego każdego dnia. :)
David
@sjas Możesz łatwo dodać \-wyrażenie regularne, aby rozwiązać problem. Służy [0-9\-\.\+]do rozliczania liczb zmiennoprzecinkowych i podpisanych numerów.
user2683246
@sjas ok, moja wina
2683246
@sjasecho $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
user2683246
9

W przypadku mojego problemu musiałem tylko upewnić się, że użytkownik nie przypadkowo wprowadzi jakiś tekst, dlatego starałem się, aby był on prosty i czytelny

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

Według strony podręcznika to właściwie robi to, czego chcę

Jeśli wartość wyrażenia jest niezerowa, zwracanym statusem jest 0

Aby zapobiec nieprzyjemnym komunikatom o błędach dla ciągów, które „mogą być liczbami”, ignoruję wynik błędu

$ (( 2s ))
bash: ((: 2s: value too great for base (error token is "2s")
Hachi
źródło
To bardziej przypomina isInteger. Ale super dzięki!
Naheel
8

Stare pytanie, ale chciałem tylko rozwiązać moje rozwiązanie. Ten nie wymaga żadnych dziwnych sztuczek z pociskami ani polegania na czymś, co nie istniało wiecznie.

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

Zasadniczo po prostu usuwa wszystkie cyfry z wejścia, a jeśli masz ciąg znaków o niezerowej długości, to nie jest to liczba.

Sammitch
źródło
To się nie powiedzie dla pustego var.
gniourf_gniourf
Lub dla zmiennych z końcowymi znakami nowej linii lub czegoś podobnego $'0\n\n\n1\n\n\n2\n\n\n3\n'.
gniourf_gniourf
7

Nie mogę jeszcze komentować, więc dodam własną odpowiedź, która jest rozszerzeniem odpowiedzi Glenna Jackmana za pomocą dopasowania wzorca bash.

Moją pierwotną potrzebą było identyfikowanie liczb oraz rozróżnianie liczb całkowitych i liczb zmiennoprzecinkowych. Definicje funkcji odliczone do:

function isInteger() {
    [[ ${1} == ?(-)+([0-9]) ]]
}

function isFloat() {
    [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}

Użyłem testów jednostkowych (z shUnit2), aby sprawdzić poprawność działania moich wzorców:

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

}

Uwagi: Wzorzec isFloat można zmodyfikować, aby był bardziej tolerancyjny w stosunku do kropki dziesiętnej ( @(.,)) i symbolu E ( @(Ee)). Moje testy jednostkowe testują tylko wartości całkowite lub zmiennoprzecinkowe, ale nie są to żadne nieprawidłowe dane wejściowe.

karttu
źródło
Przepraszam, nie rozumiem, jak ma być używana funkcja edycji.
3ronco,
6

Spróbowałbym tego:

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

Uwaga: rozpoznaje to nan i inf jako liczbę.

przepełnione
źródło
2
albo duplikat, albo być może lepiej nadaje się jako komentarz do odpowiedzi pixelbeat (użycie i tak %fjest prawdopodobnie lepsze)
michael
3
Zamiast sprawdzać poprzedni kod stanu, dlaczego po prostu nie umieścić go w ifsobie? Właśnie to if...if printf "%g" "$var" &> /dev/null; then ...
Camilo Martin
3
Ma to inne zastrzeżenia. Sprawdza poprawność pustego łańcucha i łańcuchów podobnych 'a.
gniourf_gniourf
6

Jasna odpowiedź została już udzielona przez @charles Dufy i innych. Rozwiązaniem czysto bashowym byłoby użycie:

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

Chociaż w przypadku liczb rzeczywistych nie jest obowiązkowe, aby mieć liczbę przed punktem bazowym .

Aby zapewnić dokładniejszą obsługę liczb zmiennoprzecinkowych i notacji naukowej (wiele programów w C / Fortran lub w inny sposób eksportuje liczby zmiennoprzecinkowe w ten sposób), przydatnym dodatkiem do tego wiersza byłoby:

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

Prowadząc w ten sposób do rozróżnienia typów liczb, jeśli szukasz jakiegoś konkretnego typu:

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

Uwaga: Moglibyśmy wymienić wymagania syntaktyczne dla notacji dziesiętnej i naukowej, przy czym jednym z nich jest zezwolenie na przecinek jako punkt radix, a także „.”. Twierdzilibyśmy wtedy, że musi istnieć tylko jeden taki punkt bazowy. Mogą występować dwa znaki +/- w pływaka [Ee]. Nauczyłem się jeszcze kilku zasad z pracy Aulu i przetestowałem na złych ciągach, takich jak „” - „” -E-1 ”„ 0-0 ”. Oto moje narzędzia regex / substring / expr, które wydają się wstrzymywać:

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
Aulo
źródło
6
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

Nie zapomnij -podać liczb ujemnych!

D_I
źródło
Jaka jest minimalna wersja bash? Właśnie dostaję bash: warunkowy operator binarny oczekiwany bash: błąd składni w pobliżu nieoczekiwanego tokena `= ~ '
Paul Hargreaves
1
@PaulHargreaves =~istniał przynajmniej od czasów bash 3.0.
Gilles „SO- przestań być zły”
@PaulHargreaves prawdopodobnie miałeś problem z pierwszym operandem, np. Zbyt wiele znaków cudzysłowu lub podobnych
Joshua Clayton
@JoshuaClayton poprosiłem o wersji, ponieważ jest to bardzo bardzo stare bash na polu Solaris 7, która wciąż mamy i nie obsługuje = ~
Paul Hargreaves
6

Można to osiągnąć, grepsprawdzając, czy dana zmienna pasuje do rozszerzonego wyrażenia regularnego.

Liczba całkowita testowa 1120:

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

Wynik: Valid number.

Testuj liczbę całkowitą 1120a:

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

Wynik: Error: not a number.


Wyjaśnienie

  • grepThe -Eprzełącznik pozwala nam korzystać z rozszerzonego wyrażenia regularnego '^[0-9]+$'. To wyrażenie regularne oznacza, że ​​zmienna powinna []zawierać tylko liczby od 0-9zera do dziewięciu od ^początku do $końca zmiennej i powinna mieć co najmniej +jeden znak.
  • grepThe -qprzełącznik cichy wyłącza każdym wyjściu czy nie stwierdzi nic.
  • ifsprawdza status wyjścia grep. Status wyjścia 0oznacza sukces, a cokolwiek większego oznacza błąd. grepKomenda ma status wyjścia 0, jeżeli stwierdzi mecz i 1gdy nie robi;

Więc łącząc to wszystko, w ifteście, my echozmienną $yournumberi |potokujemy ją, do grepktórej -qprzełącznikiem po cichu dopasowuje -Erozszerzone wyrażenie regularne '^[0-9]+$'. Status wyjścia grepbędzie, 0jeśli grepuda się znaleźć dopasowanie, a 1jeśli nie. Jeśli uda się dopasować, my echo "Valid number.". Jeśli to nie pasuje, my echo "Error: not a number.".


Dla pływaków lub podwajaczy

Możemy tylko zmienić wyrażenie regularne od '^[0-9]+$'celu '^[0-9]*\.?[0-9]+$'dla pływaków lub dwuosobowe.

Pływak testowy 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

Wynik: Valid number.

Pływak testowy 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

Wynik: Error: not a number.


Dla negatywów

Aby zezwolić na ujemne liczby całkowite, wystarczy zmienić wyrażenie regularne z '^[0-9]+$'na '^\-?[0-9]+$'.

Aby zezwolić na liczby zmiennoprzecinkowe lub podwójne, po prostu zmień wyrażenie regularne z '^[0-9]*\.?[0-9]+$'na '^\-?[0-9]*\.?[0-9]+$'.

Joseph Shih
źródło
Masz rację, @CharlesDuffy. Dzięki. Oczyściłem odpowiedź.
Joseph Shih
Masz rację, @CharlesDuffy. Naprawiony!
Joseph Shih
1
LGTM; odpowiedź po redakcji ma moje +1. Jedyne rzeczy, które zrobiłbym inaczej w tym momencie, to tylko kwestia opinii, a nie poprawności (f / e, używanie [-]zamiast \-i [.]zamiast \.jest nieco bardziej szczegółowe, ale oznacza to, że twoje łańcuchy nie muszą się zmieniać, jeśli „ ponownie użyte w kontekście, w którym zużywane są ukośniki odwrotne).
Charles Duffy
4

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html

Możesz także użyć klas postaci basha.

if [[ $VAR = *[[:digit:]]* ]]; then
 echo "$VAR is numeric"
else
 echo "$VAR is not numeric"
fi

Numeryczne będą zawierać spację, kropkę dziesiętną oraz „e” lub „E” dla liczby zmiennoprzecinkowej.

Ale jeśli podasz liczbę szesnastkową w stylu C, tj. „0xffff” lub „0XFFFF”, [[: digit:]] zwróci true. Trochę pułapki tutaj, bash pozwala ci zrobić coś takiego jak „0xAZ00” i nadal liczyć to jako cyfrę (czy nie jest to dziwne dziwactwo kompilatorów GCC, które pozwalają używać notacji 0x dla baz innych niż 16 ??? )

Możesz przetestować na „0x” lub „0X” przed przetestowaniem, czy jest to wartość liczbowa, jeśli dane wejściowe są całkowicie niezaufane, chyba że chcesz zaakceptować liczby szesnastkowe. Można to osiągnąć poprzez:

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi
ultrasawblade
źródło
17
[[ $VAR = *[[:digit:]]* ]]zwróci true, jeśli zmienna zawiera liczbę, a nie jeśli jest liczbą całkowitą.
glenn jackman
[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"odciski numeric. Testowany w wersji bash 3.2.25(1)-release.
Jdamian
1
@ultraswadable, twoje rozwiązanie wykrywa ciągi zawierające co najmniej jedną cyfrę otoczoną (lub nie) innymi znakami. Przegłosowałem.
Jdamian
Oczywiście poprawnym podejściem jest zatem odwrócenie tego i użycie[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
eschwartz
@eschwartz, twoje rozwiązanie nie działa z liczbami ujemnymi
Angel
4

Najprostszym sposobem jest sprawdzenie, czy zawiera znaki niecyfrowe. Wszystkie cyfry zamieniasz na nic i sprawdzasz długość. Jeśli jest długość, nie jest to liczba.

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
Andy
źródło
2
Obsługa liczb ujemnych wymagałaby bardziej skomplikowanego podejścia.
Andy,
... Lub opcjonalny znak pozytywny.
tripleee
@tripleee Chciałbym zobaczyć twoje podejście, jeśli wiesz, jak to zrobić.
Andy
4

Ponieważ ostatnio musiałem to manipulować i najbardziej podobało mi się podejście karttu do testu jednostkowego. Zmodyfikowałem kod i dodałem też kilka innych rozwiązań, wypróbuj go sam, aby zobaczyć wyniki:

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

Tak więc isNumber () zawiera myślniki, przecinki i notację wykładniczą, a zatem zwraca PRAWDA na liczbach całkowitych i liczbach zmiennoprzecinkowych, gdzie z drugiej strony isFloat () zwraca wartość FAŁSZ na liczbach całkowitych, a isInteger () podobnie zwraca wartość FAŁSZ na liczbach zmiennoprzecinkowych . Dla Twojej wygody wszystko jako jedna wkładka:

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; }
3ronco
źródło
Osobiście chciałbym usunąć functionsłowo kluczowe, ponieważ nie coś pożytecznego zrobić. Ponadto, nie jestem pewien co do przydatności wartości zwracanych. O ile nie określono inaczej, funkcje zwracają status wyjścia ostatniego polecenia, więc nie musisz returnnic robić sam.
Tom Fenech
Fajnie, rzeczywiście returnsą mylące i sprawiają, że jest mniej czytelny. Używanie functionsłów kluczowych lub nie jest bardziej kwestią osobistego smaku, przynajmniej usunąłem je z jednej wkładki, aby zaoszczędzić trochę miejsca. dzięki.
3ronco,
Nie zapominaj, że po testach dla wersji jednowierszowych potrzebne są średniki.
Tom Fenech
2
isNumber zwróci „true” dla każdego łańcucha zawierającego liczbę.
DrStrangepork,
@DrStrangepork Rzeczywiście, w mojej tablicy false_values ​​brakuje tego przypadku. Zajmę się tym. Dzięki za podpowiedź.
3ronco
4

Używam wyrażenie . Zwraca niezerową, jeśli spróbujesz dodać zero do wartości nienumerycznej:

if expr -- "$number" + 0 > /dev/null 2>&1
then
    echo "$number is a number"
else
    echo "$number isn't a number"
fi

Może być możliwe użycie bc, jeśli potrzebujesz liczb całkowitych, ale nie sądzę bc, że zachowuje się tak samo. Dodanie zera do nieliczbowej daje zero i zwraca również wartość zero. Może możesz połączyć bci expr. Użyj, bcaby dodać zero do $number. Jeśli odpowiedź brzmi 0, spróbuj exprsprawdzić, czy $numbernie jest to zero.

David W.
źródło
1
To jest raczej złe. Aby uczynić go nieco lepiej należy użyć expr -- "$number" + 0; ale to i tak będzie udawać 0 isn't a number. Z man expr:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
gniourf_gniourf
3

Podoba mi się odpowiedź Alberto Zaccagniego.

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

Ważne warunki wstępne: - brak odrodzenia podpowłoki - brak wywoływanych parserów RE - większość aplikacji powłoki nie używa liczb rzeczywistych

Jednak, jeśli $varjest złożona (np asocjacyjny dostęp do tablicy), a jeśli liczba będzie nieujemną liczbę całkowitą (w większości przypadków użycia), to jest być może bardziej skuteczne?

if [ "$var" -ge 0 ] 2> /dev/null; then ..
użytkownik3895088
źródło
2

Używam printf jako innych wspomnianych odpowiedzi, jeśli podasz ciąg formatu „% f” lub „% i” printf zrobi to za ciebie. Łatwiej niż na nowo wymyślić kontrole, składnia jest prosta i krótka, a printf jest wszechobecny. Moim zdaniem jest to więc dobry wybór - możesz również skorzystać z następującego pomysłu, aby sprawdzić zakres rzeczy, nie tylko przydaje się do sprawdzania liczb.

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; }


źródło
2

wyrażenie regularne vs rozszerzenie parametru

Jak odpowiedź Charlesa Duffy działa, ale tylko użyjwyrażenie regularne i wiem, że jest to powolne, chciałbym pokazać inny sposób, używając tylkorozszerzenie parametru :

Tylko dla dodatnich liczb całkowitych:

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

Dla liczb całkowitych:

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

Następnie dla pływających:

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

Aby dopasować początkowe żądanie:

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

Teraz małe porównanie:

Ta sama kontrola opiera się na odpowiedzi Charlesa Duffy'ego :

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

Niektóre testy:

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

W porządku. Ile czasu zajmie to wszystko (na moim malinowym pi):

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

Następnie z wyrażeniami regularnymi:

time for i in {1..1000};do cdIs_num +3.14159265;done
real    0m4.363s
user    0m2.168s
sys     0m0.000s
F. Hauri
źródło
Zgadzam się, zresztą wolę nie używać wyrażenia regularnego, gdybym mógł użyć rozszerzenia parametrów ... Nadużycie RE spowoduje spowolnienie skryptu bash
F. Hauri
Edytowana odpowiedź: Nie ma potrzeby extglob(i trochę szybciej)!
F. Hauri
@CharlesDuffy, Na moim malinowym pi, 1000 powtórzeń zajmie 2,5 sekundy w mojej wersji i 4,4 sekundy w twojej wersji!
F. Hauri
Nie mogę się z tym kłócić. 👍
Charles Duffy
1

Aby złapać liczby ujemne:

if [[ $1 == ?(-)+([0-9.]) ]]
    then
    echo number
else
    echo not a number
fi
użytkownik 28490
źródło
Wymaga to również włączenia rozszerzonego globowania. Jest to funkcja tylko dla Bash, która jest domyślnie wyłączona.
tripleee
@tripleee Extended globbing jest aktywowany automatycznie przy użyciu == lub! = 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
Badr Elmers
@BadrElmers Dzięki za aktualizację. To wydaje się być nowe zachowanie, które nie jest prawdą w moim Bash 3.2.57 (MacOS Mojave). Widzę, że działa tak, jak opisano w 4.4.
tripleee
1

Możesz też użyć „let” w ten sposób:

[ ~]$ 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
[ ~]$ 

Ale wolę używać operatora „= ~” Bash 3+ jak niektórych odpowiedzi w tym wątku.

Idriss Neumann
źródło
5
To jest bardzo niebezpieczne. Nie oceniaj nieważonej arytmetyki w powłoce. Najpierw należy to sprawdzić w inny sposób.
ormaaj,
1
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

Usuń -\?wzór dopasowania grep, jeśli nie akceptujesz ujemnej liczby całkowitej.

Ane Dijitak
źródło
2
Głosuj za brakiem wyjaśnień. Jak to działa? Wygląda na skomplikowane i kruche i nie jest oczywiste, jakie dane wejściowe akceptuje. (Na przykład, czy usunięcie spacji jest absolutnie konieczne? Dlaczego? Powie, że liczba z osadzonymi spacjami jest poprawną liczbą, co może nie być pożądane.)
tripleee
1

Zrobił to samo z wyrażeniem regularnym, które testuje całą część i część dziesiętną, oddzielone kropką.

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

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi
Jerome
źródło
Czy możesz wyjaśnić, dlaczego twoja odpowiedź zasadniczo różni się od innych starych odpowiedzi, np. Odpowiedzi Charlesa Duffy'ego? Cóż, twoja odpowiedź jest właściwie zepsuta, ponieważ potwierdza pojedynczy okres.
gniourf_gniourf
Nie jestem pewien, czy rozumiem tutaj pojedynczy okres ... jest to jeden lub zero, oczekiwany okres .... Ale w zasadzie nic zasadniczo innego, po prostu stwierdziłem, że regex jest łatwiejszy do odczytania.
Jerome
także użycie * powinno pasować do większej liczby rzeczywistych przypadków
Jerome
Chodzi o to, że dopasowujesz pusty ciąg a=''i ciąg zawierający tylko kropkę, a='.'więc kod jest nieco zepsuty ...
gniourf_gniourf
0

Używam następujących (dla liczb całkowitych):

## ##### 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
Marnix
źródło
Prawie poprawne (akceptujesz pusty ciąg), ale z wdzięcznością skomplikowane, aż do zaciemnienia.
Gilles „SO- przestań być zły”
2
Niepoprawnie: akceptujesz -nitp. (Z powodu echo) i akceptujesz zmienne z końcowymi znakami nowej linii (z powodu $(...)). Nawiasem mówiąc, printnie jest prawidłowym poleceniem powłoki.
gniourf_gniourf
0

Wypróbowałem przepis ultrasawblade, który wydawał mi się najbardziej praktyczny i nie mogłem go uruchomić. W końcu jednak wymyśliłem inny sposób, oparty na podstawieniu parametrów, tym razem z zastąpieniem wyrażeń regularnych:

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

Usuwa każdy znak: digit: class w $ var i sprawdza, czy został nam pusty ciąg znaków, co oznacza, że ​​oryginał zawierał tylko liczby.

Najbardziej podoba mi się jego niewielka powierzchnia i elastyczność. W tej formie działa tylko w przypadku liczb całkowitych nieokreślonych liczbami podstawowymi, ale z pewnością można użyć dopasowania wzorca, aby dopasować go do innych potrzeb.

ata
źródło
Czytając rozwiązanie mrucciego, wygląda prawie tak samo jak mój, ale używa zwykłej zamiany strun zamiast „stylu sed”. Oba stosują te same zasady przy dopasowywaniu wzorców i są, AFAIK, wymiennymi rozwiązaniami.
ata
sedjest POSIX, a twoim rozwiązaniem jest bash. Oba mają swoje zastosowania
v010dya
0

Quick & Dirty: Wiem, że nie jest to najbardziej elegancki sposób, ale zwykle po prostu dodałem do niego zero i testowałem wynik. tak:

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)) zwróci 0 lub bombę, jeśli 1 $ NIE jest liczbą całkowitą. na przykład:

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
}

UWAGA: Wydaje mi się, że nigdy nie myślałem, aby sprawdzić zmiennoprzecinkowe lub mieszane typy, które spowodowałyby, że cały skrypt bomby… w moim przypadku nie chciałem, aby poszedł dalej. Pobawię się rozwiązaniem Mrucciego i regexem Duffy'ego - wydają się najbardziej niezawodne w ramach bash ...

WWWIZARDS
źródło
4
Akceptuje to wyrażenia arytmetyczne podobne do 1+1, ale odrzuca niektóre dodatnie liczby całkowite z wiodącymi 0s (ponieważ 08jest to niepoprawna stała ósemkowa).
Gilles „SO- przestań być zły”
2
To ma też inne problemy: 0nie jest liczbą, i to jest przedmiotem arbitralnej wstrzyknięcia kodu, spróbuj go: isInteger 'a[$(ls)]'. Ups
gniourf_gniourf
1
A rozszerzenie $((...))nie jest cytowane, cyfra IFS=123to zmieni.
Izaak