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 1fi
Jeśli wartość niekoniecznie jest liczbą całkowitą, rozważ odpowiednią zmianę wyrażenia regularnego; na przykład:
+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 ( ?*-*/ *.*.*)
+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.
@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")
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";
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”.
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}
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:
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
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
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.
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")
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.
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}"donefor 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.
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ć:
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=1120if 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=1120aif 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.01if 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.01if 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]+$'.
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).
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
[[ $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
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:
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:
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>&1then
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.
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?
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=1local-i FAIL=0case"${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; }
Jak odpowiedź Charlesa Duffy działa, ale tylko użyjgrzmotnąćwyrażenie regularne i wiem, że jest to powolne, chciałbym pokazać inny sposób, używając tylkogrzmotnąćrozszerzenie parametru :
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
if is_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fiIt'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;fiNot a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fiIt'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
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
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.
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ą.
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
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.
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
return1fi
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 ...
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.
test && echo "foo" && exit 0 || echo "bar" && exit 1
podejś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 0
zostanie ono pominięte, a kod spróbuje to zrobićecho "bar"
. Jeśli to też zawiedzie,&&
warunek się nie powiedzie i nawet się nie wykonaexit 1
! Używanie rzeczywistychif
instrukcji zamiast&&
/||
jest mniej podatne na nieoczekiwane skutki uboczne.[[ $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ćbash
wydrukowanie najbrzydszych (i najbardziej bezcelowych) komunikatów o błędach ...[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Odpowiedzi:
Jednym z podejść jest użycie wyrażenia regularnego, takiego jak:
Jeśli wartość niekoniecznie jest liczbą całkowitą, rozważ odpowiednią zmianę wyrażenia regularnego; na przykład:
... lub, aby obsługiwać liczby ze znakiem:
źródło
^-?
,^-*
chyba że faktycznie wykonujesz pracę, aby poprawnie obsłużyć wiele inwersji.[[ $yournumber =~ ^[0-9]+$ ]]
.Bez baszizmów (działa nawet w Systemie V sh),
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 (?*-*
/*.*.*
)źródło
if test ...
${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.'.' | *.*.*
do niedozwolonych wzorów i dodać kropkę do dozwolonych znaków. Podobnie możesz wcześniej zezwolić na znak opcjonalny, chociaż wtedy wolałbymcase ${string#[-+]}
po prostu zignorować znak.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:
jak w:
Możesz także przetestować za $? kod powrotu operacji, który jest bardziej wyraźny:
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):
[[ ]]
zamiast[ ]
zawsze zawsze będzie oznaczaćtrue
true
bash: [[: 1 a: syntax error in expression (error token is "a")
bash: [[: i: expression recursion level exceeded (error token is "i")
źródło
[[ a -eq a ]]
if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
[
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.[[ ]]
ale nie dla[ ]
. To powiedziawszy, takie zachowanie nie jest określone zarówno przez standard POSIX, jaktest
i 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.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.
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.
źródło
*[!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śćnum
zmiennej 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.122s
:-(
Nikt nie sugerował rozszerzonego dopasowania wzoru bash :
źródło
shopt -s extglob
usuwam 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 jakbyextglob
opcja powłoki była włączona. Mam nadzieję, że nie masz nic przeciwko!shopt extglob
... dobrze wiedzieć![[...]]
nie podlegają podziałowi słów ani rozszerzaniu globu.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:
Zmień '% f' na dowolny wymagany format.
źródło
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
isnumber 23 && echo "this is a number" || echo "not a number"
2>/dev/null
, abyisnumber "foo"
nie zanieczyszczało stderr?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 .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:
Używanie w skrypcie bash:
Aby dopasować liczby całkowite JUST, użyj tego:
źródło
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:
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:
źródło
'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 -q
itp${i//[0-9]}
zastępuje dowolną cyfrę wartości$i
pustym łańcuchem, patrzman -P 'less +/parameter\/' bash
.-z
sprawdza, czy wynikowy łańcuch ma zerową długość.jeśli chcesz również wykluczyć przypadek, gdy
$i
jest pusty, możesz użyć jednej z tych konstrukcji:źródło
man -P 'less +/parameter\/' bash
część. Uczenie się czegoś nowego każdego dnia. :)\-
wyrażenie regularne, aby rozwiązać problem. Służy[0-9\-\.\+]
do rozliczania liczb zmiennoprzecinkowych i podpisanych numerów.echo $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
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
Według strony podręcznika to właściwie robi to, czego chcę
Aby zapobiec nieprzyjemnym komunikatom o błędach dla ciągów, które „mogą być liczbami”, ignoruję wynik błędu
źródło
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.
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.
źródło
var
.$'0\n\n\n1\n\n\n2\n\n\n3\n'
.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:
Użyłem testów jednostkowych (z shUnit2), aby sprawdzić poprawność działania moich wzorców:
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.źródło
Spróbowałbym tego:
Uwaga: rozpoznaje to nan i inf jako liczbę.
źródło
%f
jest prawdopodobnie lepsze)if
sobie? Właśnie toif
...if printf "%g" "$var" &> /dev/null; then ...
'a
.Jasna odpowiedź została już udzielona przez @charles Dufy i innych. Rozwiązaniem czysto bashowym byłoby użycie:
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:
Prowadząc w ten sposób do rozróżnienia typów liczb, jeśli szukasz jakiegoś konkretnego typu:
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ć:
źródło
Nie zapomnij
-
podać liczb ujemnych!źródło
=~
istniał przynajmniej od czasów bash 3.0.Można to osiągnąć,
grep
sprawdzając, czy dana zmienna pasuje do rozszerzonego wyrażenia regularnego.Liczba całkowita testowa
1120
:Wynik:
Valid number.
Testuj liczbę całkowitą
1120a
:Wynik:
Error: not a number.
Wyjaśnienie
grep
The-E
przełącznik pozwala nam korzystać z rozszerzonego wyrażenia regularnego'^[0-9]+$'
. To wyrażenie regularne oznacza, że zmienna powinna[]
zawierać tylko liczby od0-9
zera do dziewięciu od^
początku do$
końca zmiennej i powinna mieć co najmniej+
jeden znak.grep
The-q
przełącznik cichy wyłącza każdym wyjściu czy nie stwierdzi nic.if
sprawdza status wyjściagrep
. Status wyjścia0
oznacza sukces, a cokolwiek większego oznacza błąd.grep
Komenda ma status wyjścia0
, jeżeli stwierdzi mecz i1
gdy nie robi;Więc łącząc to wszystko, w
if
teście, myecho
zmienną$yournumber
i|
potokujemy ją, dogrep
której-q
przełącznikiem po cichu dopasowuje-E
rozszerzone wyrażenie regularne'^[0-9]+$'
. Status wyjściagrep
będzie,0
jeśligrep
uda się znaleźć dopasowanie, a1
jeśli nie. Jeśli uda się dopasować, myecho "Valid number."
. Jeśli to nie pasuje, myecho "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
:Wynik:
Valid number.
Pływak testowy
11.20.01
: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]+$'
.źródło
[-]
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).http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
Możesz także użyć klas postaci basha.
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:
źródło
[[ $VAR = *[[:digit:]]* ]]
zwróci true, jeśli zmienna zawiera liczbę, a nie jeśli jest liczbą całkowitą.[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"
odciskinumeric
. Testowany w wersji bash3.2.25(1)-release
.[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
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.
źródło
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:
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:
źródło
function
sł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 musiszreturn
nic robić sam.return
są mylące i sprawiają, że jest mniej czytelny. Używaniefunction
słów kluczowych lub nie jest bardziej kwestią osobistego smaku, przynajmniej usunąłem je z jednej wkładki, aby zaoszczędzić trochę miejsca. dzięki.Używam wyrażenie . Zwraca niezerową, jeśli spróbujesz dodać zero do wartości nienumerycznej:
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ćbc
iexpr
. Użyj,bc
aby dodać zero do$number
. Jeśli odpowiedź brzmi0
, spróbujexpr
sprawdzić, czy$number
nie jest to zero.źródło
expr -- "$number" + 0
; ale to i tak będzie udawać0 isn't a number
. Zman expr
:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
Podoba mi się odpowiedź Alberto Zaccagniego.
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
$var
jest 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?źródło
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.
>$ var=45
>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }
źródło
grzmotnąć wyrażenie regularne vsgrzmotnąć rozszerzenie parametru
Jak odpowiedź Charlesa Duffy działa, ale tylko użyjgrzmotnąćwyrażenie regularne i wiem, że jest to powolne, chciałbym pokazać inny sposób, używając tylkogrzmotnąćrozszerzenie parametru :
Tylko dla dodatnich liczb całkowitych:
Dla liczb całkowitych:
Następnie dla pływających:
Aby dopasować początkowe żądanie:
Teraz małe porównanie:
Ta sama kontrola opiera się na odpowiedzi Charlesa Duffy'ego :
Niektóre testy:
W porządku. Ile czasu zajmie to wszystko (na moim malinowym pi):
Następnie z wyrażeniami regularnymi:
źródło
extglob
(i trochę szybciej)!Aby złapać liczby ujemne:
źródło
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_005bMożesz też użyć „let” w ten sposób:
Ale wolę używać operatora „= ~” Bash 3+ jak niektórych odpowiedzi w tym wątku.
źródło
Usuń
-\?
wzór dopasowania grep, jeśli nie akceptujesz ujemnej liczby całkowitej.źródło
Zrobił to samo z wyrażeniem regularnym, które testuje całą część i część dziesiętną, oddzielone kropką.
źródło
.
a=''
i ciąg zawierający tylko kropkę,a='.'
więc kod jest nieco zepsuty ...Używam następujących (dla liczb całkowitych):
źródło
-n
itp. (Z powoduecho
) i akceptujesz zmienne z końcowymi znakami nowej linii (z powodu$(...)
). Nawiasem mówiąc,print
nie jest prawidłowym poleceniem powłoki.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:
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.
źródło
sed
jest POSIX, a twoim rozwiązaniem jestbash
. Oba mają swoje zastosowaniaQuick & Dirty: Wiem, że nie jest to najbardziej elegancki sposób, ale zwykle po prostu dodałem do niego zero i testowałem wynik. tak:
$ ((1 $ + 0)) zwróci 0 lub bombę, jeśli 1 $ NIE jest liczbą całkowitą. na przykład:
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 ...
źródło
1+1
, ale odrzuca niektóre dodatnie liczby całkowite z wiodącymi0
s (ponieważ08
jest to niepoprawna stała ósemkowa).0
nie jest liczbą, i to jest przedmiotem arbitralnej wstrzyknięcia kodu, spróbuj go:isInteger 'a[$(ls)]'
. Ups$((...))
nie jest cytowane, cyfraIFS=123
to zmieni.