Próbuję zrobić coś dość powszechnego: przeanalizować dane wejściowe użytkownika w skrypcie powłoki. Jeśli użytkownik podał prawidłową liczbę całkowitą, skrypt robi jedną rzecz, a jeśli nie jest poprawny, robi coś innego. Kłopot w tym, że nie znalazłem łatwego (i dość eleganckiego) sposobu na zrobienie tego - nie chcę, aby trzeba było go rozdzielać po znaku.
Wiem, że to musi być łatwe, ale nie wiem jak. Mógłbym to zrobić w kilkunastu językach, ale nie w BASH!
W moich badaniach znalazłem to:
I jest w tym odpowiedź, która mówi o wyrażeniu regularnym, ale o ile wiem, jest to funkcja dostępna w C (między innymi). Mimo to miał coś, co wyglądało na świetną odpowiedź, więc spróbowałem go z grep, ale grep nie wiedział, co z tym zrobić. Spróbowałem -P, co na moim pudełku oznacza traktowanie go jako wyrażenia regularnego PERL - nada. Dash E (-E) też nie działał. Ani też -F.
Żeby było jasne, próbuję czegoś takiego, szukając jakichkolwiek wyników - stamtąd zhakuję skrypt, aby wykorzystać wszystko, co otrzymam. (IOW, spodziewałem się, że niezgodne dane wejściowe nic nie zwracają, podczas gdy poprawna linia jest powtarzana.)
snafu=$(echo "$2" | grep -E "/^[-+]?(?:\.[0-9]+|(?:0|[1-9][0-9]*)(?:\.[0-9]*)?)$/")
if [ -z "$snafu" ] ;
then
echo "Not an integer - nothing back from the grep"
else
echo "Integer."
fi
Czy ktoś mógłby zilustrować, jak najłatwiej to zrobić?
Szczerze mówiąc, jest to moim zdaniem niedostatek TESTU. Powinien mieć taką flagę
if [ -I "string" ] ;
then
echo "String is a valid integer."
else
echo "String is not a valid integer."
fi
źródło
[
jest stary kompatybilnytest
;[[
to nowa rzecz Bash, z większą liczbą operacji i innymi zasadami cytowania. Jeśli już zdecydowałeś się pozostać przy Bashu, idź[[
(jest o wiele ładniejszy); jeśli potrzebujesz możliwości przenoszenia na inne muszle,[[
całkowicie unikaj .Odpowiedzi:
^
wskazuje początek wzorca wejściowego-
jest dosłowne „-”?
środki „0 lub 1 z poprzedzającego (-
)”+
środki „1 lub więcej z powyższych ([0-9]
)”$
wskazuje koniec wzorca wejściowegoZatem wyrażenie regularne dopasowuje opcjonalne
-
(w przypadku liczb ujemnych), po którym następuje jedna lub więcej cyfr dziesiętnych.Referencje :
źródło
+
oznacza „jeden lub więcej z poprzednich” i$
wskazuje na koniec wzorca sygnału. Zatem wyrażenie regularne dopasowuje opcjonalne,-
po którym następuje jedna lub więcej cyfr dziesiętnych.[A-z]
nie tylko dajeA-Z
, aa-z
jednak również\
,[
,]
,^
,_
, i`
.d[g-i]{2}
może zakończyć się nie tylko dopasowaniem,dig
ale także porównaniemdish
sugerowanym przez tę odpowiedź (gdziesh
dwuznak jest traktowany jako pojedynczy znak, zestawiony poh
).Wow ... jest tu tak wiele dobrych rozwiązań !! Ze wszystkich powyższych rozwiązań zgadzam się z @nortally, że używanie
-eq
one linera jest najfajniejsze.Używam GNU bash, wersja
4.1.5
(Debian). Sprawdziłem to również na ksh (SunSO 5.10).Oto moja wersja sprawdzania, czy
$1
jest liczbą całkowitą, czy nie:Podejście to uwzględnia również liczby ujemne, które w przypadku niektórych innych rozwiązań będą miały błędny wynik ujemny i pozwoli na przedrostek „+” (np. +30), który oczywiście jest liczbą całkowitą.
Wyniki:
Rozwiązanie dostarczone przez Ignacio Vazquez-Abrams było również bardzo zgrabne (jeśli lubisz regex) po tym, jak zostało wyjaśnione. Jednak nie obsługuje liczb dodatnich z
+
prefiksem, ale można go łatwo naprawić, jak poniżej:źródło
Spóźniony na przyjęcie tutaj. Jestem niezwykle zaskoczony, że żadna z odpowiedzi nie wspomina o najprostszym, najszybszym i najbardziej przenośnym rozwiązaniu;
case
stwierdzenie.Przycinanie dowolnego znaku przed porównaniem wydaje się trochę hackem, ale to sprawia, że wyrażenie w instrukcji case jest o wiele prostsze.
źródło
''|*[!0-9]*)
Podoba mi się rozwiązanie wykorzystujące
-eq
test, ponieważ jest to w zasadzie jednolinijkowy.Moje własne rozwiązanie polegało na zastosowaniu rozszerzenia parametrów, aby wyrzucić wszystkie cyfry i sprawdzić, czy coś zostało. (Nadal używam 3.0, nie używałem
[[
aniexpr
wcześniej, ale cieszę się, że je spotykam.)źródło
[ -z "${INPUT_STRING//[0-9]}" ]
ale naprawdę fajnego rozwiązania!-eq
Rozwiązanie ma pewne problemy; patrz tutaj: stackoverflow.com/a/808740/1858225Aby zapewnić przenośność do wersji pre-Bash 3.1 (gdy
=~
test został wprowadzony), użyjexpr
.expr STRING : REGEX
wyszukuje REGEX zakotwiczony na początku ŁAŃCUCHA, powtarzając pierwszą grupę (lub długość dopasowania, jeśli nie ma) i zwracając sukces / niepowodzenie. To jest stara składnia wyrażeń regularnych, stąd nadmiar\
.-\?
oznacza „być może-
”,[0-9]\+
oznacza „jedną lub więcej cyfr” i$
oznacza „koniec łańcucha”.Bash obsługuje również rozszerzone globi, chociaż nie pamiętam, od której wersji.
@(-|)
oznacza „-
albo nic”,[0-9]
oznacza „cyfrę” i*([0-9])
oznacza „zero lub więcej cyfr”.źródło
awk
,~
był operator „regex mecz”. W Perlu (skopiowanym z C)~
był już używany do „bitowego uzupełnienia”, więc używali=~
. Ten późniejszy zapis został skopiowany do kilku innych języków. (Perl 5.10 i Perl 6 lubią~~
więcej, ale to nie ma tu żadnego wpływu.) Przypuszczam, że można to potraktować jako pewnego rodzaju przybliżoną równość ...Oto jeszcze jedno podejście (tylko przy użyciu wbudowanego polecenia test i jego kodu powrotu):
źródło
$()
zif
. Działa to:if is_int "$input"
. Ponadto,$[]
forma jest przestarzała. Użyj$(())
zamiast tego. Wewnątrz również znak dolara można pominąć:echo "Integer: $((input))"
nawiasy klamrowe nie są potrzebne nigdzie w skrypcie.test
wydaje się, że to nie obsługuje.[[
jednak robi.[[ 16#aa -eq 16#aa ]] && echo integer
wypisuje "integer".[[
zwraca fałszywe alarmy dla tej metody; np[[ f -eq f ]]
. sukces. Musi więc użyćtest
lub[
.Możesz usunąć niecyfry i zrobić porównanie. Oto skrypt demonstracyjny:
Oto jak wygląda wynik testu:
źródło
${var//string}
oraz${var#string}
w sekcji „Dopasowywanie wzorców” dla [^ [: digit:]] `(co jest również omówione wman 7 regex
).match=${match#0*}
sposób nie taśmy zer, to taśmy co najwyżej jedno zero. Korzystając z rozszerzenia, można to osiągnąć tylko zaextglob
pomocąmatch=${match##+(0)}
.09
nie jest liczbą całkowitą, jeśli uważasz, że liczba całkowita nie ma zer wiodących. Test sprawdza, czy input (09
) jest równy wersji oczyszczonej (9
- liczba całkowita), a tak nie jest.Dla mnie najprostszym rozwiązaniem było użycie zmiennej wewnątrz
(())
wyrażenia, tak jak:Oczywiście to rozwiązanie jest poprawne tylko wtedy, gdy wartość zero nie ma sensu dla twojej aplikacji. Tak się stało w moim przypadku i jest to znacznie prostsze niż inne rozwiązania.
Jak wskazano w komentarzach, może to narazić Cię na atak polegający na wykonaniu kodu:
(( ))
Operator oceniaVAR
, jak stwierdzono wArithmetic Evaluation
sekcji strony podręcznika bash (1) . Dlatego nie powinieneś używać tej techniki, gdy źródło treściVAR
jest niepewne (ani oczywiście nie powinieneś używać ŻADNEJ innej formy rozwijania zmiennych).źródło
if (( var )); then echo "$var is an int."; fi
VAR='a[$(ls)]'; if ((VAR > 0)); then echo "$VAR is a positive integer"; fi
. W tym momencie cieszysz się, że zamiast tego nie wydałem złego polecenials
. Ponieważ OP wspomina o danych wejściowych użytkownika , mam nadzieję, że nie używasz tego z danymi wejściowymi użytkownika w kodzie produkcyjnym!agent007
lub z sed:
źródło
test -z "${string//[0-9]/}" && echo "integer" || echo "no integer"
… chociaż to w zasadzie powiela odpowiedź Dennisa Williamsonaif [[ -n "$(printf "%s" "${2}" | sed s/[0-9]//g)" ]]; then
Dodając do odpowiedzi Ignacio Vazquez-Abrams. Umożliwi to znak + poprzedzający liczbę całkowitą i dowolną liczbę zer jako miejsc dziesiętnych. Na przykład pozwoli to na uznanie +45,00000000 za liczbę całkowitą.
Jednak $ 1 musi być sformatowany tak, aby zawierał kropkę dziesiętną. 45 nie jest tutaj uważane za liczbę całkowitą, ale 45.0 jest.
źródło
^[-+]?[0-9]
...?Dla śmiechu z grubsza po prostu szybko opracowałem zestaw funkcji do tego (is_string, is_int, is_float, is alpha string lub other), ale są bardziej wydajne (mniej kodu) sposoby na zrobienie tego:
Przeprowadziłem tutaj kilka testów, zdefiniowałem, że -44 to int, ale 44- nie jest itd.:
Wynik:
UWAGA: Wiodące zera mogą wywnioskować coś innego podczas dodawania liczb, takich jak ósemkowe, więc lepiej byłoby je usunąć, jeśli zamierzasz traktować '09' jako int (co robię) (np.
expr 09 + 0
Lub usuń sed)źródło