Abgesehen davon - der von test && echo "foo" && exit 0 || echo "bar" && exit 1Ihnen verwendete Ansatz kann einige unbeabsichtigte Nebenwirkungen haben - wird das Echo exit 0übersprungen (möglicherweise wird es an einen geschlossenen FD ausgegeben), das wird übersprungen, und der Code versucht es dann echo "bar". Wenn dies auch fehlschlägt, schlägt die &&Bedingung fehl und wird nicht einmal ausgeführt exit 1! Die Verwendung tatsächlicher ifAussagen anstelle von &&/ ||ist weniger anfällig für unerwartete Nebenwirkungen.
Charles Duffy
@CharlesDuffy Das ist die Art von wirklich klugem Denken, zu dem die meisten Leute nur kommen, wenn sie haarige Käfer aufspüren müssen ...! Ich hätte nie gedacht, dass Echo einen Fehler zurückgeben könnte.
Camilo Martin
6
Etwas spät zur Party, aber ich weiß um die Gefahren, über die Charles geschrieben hat, da ich sie auch schon vor einiger Zeit durchgehen musste. Hier ist eine 100% narrensichere (und gut lesbare) Zeile für Sie: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }Die geschweiften Klammern zeigen an, dass Dinge NICHT in einer Unterschale ausgeführt werden sollten (was definitiv so wäre, wenn ()stattdessen Klammern verwendet würden). Vorsichtsmaßnahme: Verpassen Sie niemals das letzte Semikolon . Andernfalls könnten Sie bashdie hässlichsten (und sinnlosesten) Fehlermeldungen
ausdrucken
5
In Ubuntu funktioniert es nur, wenn Sie die Anführungszeichen entfernen. So sollte es einfach sein[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Treviño
4
Sie müssen genauer angeben, was Sie unter "Zahl" verstehen . Eine ganze Zahl? Eine Festkommazahl? Wissenschaftliche ("e") Notation? Gibt es einen erforderlichen Bereich (z. B. einen vorzeichenlosen 64-Bit-Wert) oder erlauben Sie eine beliebige Zahl, die geschrieben werden kann?
Toby Speight
Antworten:
803
Ein Ansatz besteht darin, einen regulären Ausdruck wie folgt zu verwenden:
re='^[0-9]+$'if![[ $yournumber =~ $re ]];then
echo "error: Not a number">&2; exit 1fi
Wenn der Wert nicht unbedingt eine Ganzzahl ist, sollten Sie die Regex entsprechend ändern. zum Beispiel:
^[0-9]+([.][0-9]+)?$
... oder um mit Zahlen mit einem Zeichen umzugehen:
+1 für diesen Ansatz, aber achten Sie auf Dezimalstellen. Führen Sie diesen Test beispielsweise mit "1.0" oder "1.0" aus und drucken Sie "Fehler: Keine Zahl".
Sourcerebels
15
Ich finde die '' exec> & 2; Echo ... '' ziemlich dumm. Nur '' echo ...> & 2 ''
lhunath
5
@ Ben willst du wirklich mehr als ein Minuszeichen verarbeiten? Ich würde es ^-?eher machen, als ^-*wenn Sie nicht tatsächlich die Arbeit machen, um mehrere Inversionen richtig zu handhaben.
Charles Duffy
4
@SandraSchlichting Lässt alle zukünftigen Ausgaben an stderr gehen. Nicht wirklich ein Punkt hier, wo es nur ein Echo gibt, aber es ist eine Gewohnheit, auf die ich mich in Fällen einlasse, in denen Fehlermeldungen mehrere Zeilen umfassen.
Charles Duffy
29
Ich bin nicht sicher, warum der reguläre Ausdruck in einer Variablen gespeichert werden muss, aber wenn dies aus Kompatibilitätsgründen erfolgt, halte ich dies nicht für erforderlich. Sie können den Ausdruck einfach direkt anwenden : [[ $yournumber =~ ^[0-9]+$ ]].
konsolebox
284
Ohne Bashismen (funktioniert sogar im System V sh),
case $string in''|*[!0-9]*) echo bad ;;*) echo good ;;esac
Dies lehnt leere Zeichenfolgen und Zeichenfolgen mit nicht-Ziffern ab und akzeptiert alles andere.
Negative oder Gleitkommazahlen erfordern zusätzliche Arbeit. Eine Idee ist, -/ .im ersten "schlechten" Muster auszuschließen und weitere "schlechte" Muster hinzuzufügen, die deren unangemessene Verwendung enthalten ( ?*-*/ *.*.*)
+1 - Dies ist ein idiomatischer, portabler Weg zurück zur ursprünglichen Bourne-Shell und bietet integrierte Unterstützung für Platzhalter im Glob-Stil. Wenn Sie aus einer anderen Programmiersprache kommen, sieht es unheimlich aus, aber es ist viel eleganter, als mit der Sprödigkeit verschiedener if test ...
Zitierprobleme
6
Sie können die erste Zeile in ${string#-}(was in antiken Bourne-Shells nicht funktioniert, aber in jeder POSIX-Shell funktioniert) ändern , um negative Ganzzahlen zu akzeptieren.
Gilles 'SO - hör auf böse zu sein'
4
Dies lässt sich auch leicht auf Floats erweitern. Fügen Sie einfach '.' | *.*.*die nicht zulässigen Muster hinzu und fügen Sie den zulässigen Zeichen einen Punkt hinzu. Ebenso können Sie vorher ein optionales Zeichen zulassen, obwohl ich es dann vorziehen würde case ${string#[-+]}, das Zeichen einfach zu ignorieren.
@Dor Die Anführungszeichen werden nicht benötigt, da der Befehl case ohnehin keine Wortaufteilung und Pfadnamengenerierung für dieses Wort durchführt. (Erweiterungen für den Fall, dass Muster möglicherweise in
Anführungszeichen gesetzt werden
193
Die folgende Lösung kann auch in Basis-Shells wie Bourne verwendet werden, ohne dass reguläre Ausdrücke erforderlich sind. Grundsätzlich führen alle numerischen Wertevaluierungsoperationen, bei denen keine Zahlen verwendet werden, zu einem Fehler, der in der Shell implizit als falsch betrachtet wird:
"$var"-eq "$var"
wie in:
#!/bin/bash
var=a
if[-n "$var"]&&["$var"-eq "$var"]2>/dev/null;then
echo number
else
echo not a number
fi
Sie können auch für $ testen? der Rückkehrcode der Operation, der expliziter ist:
[-n "$var"]&&["$var"-eq "$var"]2>/dev/null
if[ $?-ne 0];then
echo $var is not number
fi
Die Umleitung des Standardfehlers dient dazu, die Meldung "Ganzzahliger Ausdruck erwartet" auszublenden, die von bash gedruckt wird, falls wir keine Nummer haben.
CAVEATS (dank der Kommentare unten):
Zahlen mit Dezimalstellen sind es nicht als gültige "Zahlen" identifiziert.
Verwenden [[ ]]statt[ ] wird immer ausgewertettrue
Die meisten Nicht-Bash-Shells bewerten diesen Ausdruck immer als true
Das Verhalten in Bash ist nicht dokumentiert und kann sich daher ohne Vorwarnung ändern
Wenn der Wert Leerzeichen nach der Zahl enthält (z. B. "1 a"), wird z bash: [[: 1 a: syntax error in expression (error token is "a")
Wenn der Wert mit var-name identisch ist (z. B. i = "i"), wird ein Fehler erzeugt, wie z bash: [[: i: expression recursion level exceeded (error token is "i")
Ich würde dies immer noch empfehlen (aber mit den Variablen für leere Zeichenfolge ermöglichen zitiert), da das Ergebnis garantiert wird verwendbar als Zahl in Bash, egal was passiert.
10.
21
Achten Sie darauf, einzelne Klammern zu verwenden. [[ a -eq a ]]wird als wahr ausgewertet (beide Argumente werden in Null umgewandelt)
Tgr
3
Sehr schön! Beachten Sie, dass dies nur für eine Ganzzahl funktioniert, nicht für eine beliebige Zahl. Ich musste nach einem einzigen Argument if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
suchen,
6
Ich würde dringend von dieser Methode abraten, da die Anzahl der Shells, deren [Einbau die Argumente als arithmetisch bewertet, nicht unerheblich ist . Das gilt sowohl für ksh93 als auch für mksh. Da diese beiden Arrays unterstützt werden, besteht außerdem eine einfache Möglichkeit zur Code-Injektion. Verwenden Sie stattdessen eine Musterübereinstimmung.
Ormaaj
3
@AlbertoZaccagni In aktuellen Versionen von bash werden diese Werte nur für, [[ ]]aber nicht für numerische Kontextregeln interpretiert [ ]. Dieses Verhalten ist jedoch sowohl im POSIX-Standard für testals auch in der eigenen Dokumentation von bash nicht spezifiziert . Zukünftige Versionen von bash könnten das Verhalten so ändern, dass es mit ksh übereinstimmt, ohne dokumentierte Verhaltensversprechen zu brechen. Daher ist es nicht garantiert, dass es sicher ist, sich auf das derzeitige Verhalten zu verlassen.
Charles Duffy
43
Dies testet, ob eine Zahl eine nicht negative Ganzzahl ist und sowohl Shell-unabhängig (dh ohne Bashismen) als auch nur Shell-integrierte Funktionen verwendet:
FALSCH.
Da diese erste Antwort (unten) Ganzzahlen mit Zeichen zulässt, solange die ersten nicht die ersten in der Variablen sind.
[-z "${num##[0-9]*}"]&& echo "is a number"|| echo "is not a number";
RICHTIG .
Wie Jilles in seiner Antwort kommentierte und vorschlug , ist dies der richtige Weg, dies mit Shell-Mustern zu tun.
[!-z "${num##*[!0-9]*}"]&& echo "is a number"|| echo "is not a number";
Dies funktioniert nicht richtig, es werden Zeichenfolgen akzeptiert, die mit einer Ziffer beginnen. Beachten Sie, dass WORD in $ {VAR ## WORD} und ähnlichem ein Shell-Muster ist, kein regulärer Ausdruck.
Jilles
2
Können Sie diesen Ausdruck bitte ins Englische übersetzen? Ich möchte es wirklich verwenden, aber ich verstehe es nicht genug, um ihm zu vertrauen, selbst nachdem ich die Bash-Manpage gelesen habe.
CivFan
2
*[!0-9]*ist ein Muster, das allen Zeichenfolgen mit mindestens einem nichtstelligen Zeichen entspricht. ${num##*[!0-9]*}ist eine "Parametererweiterung", bei der wir den Inhalt der numVariablen nehmen und die längste Zeichenfolge entfernen, die dem Muster entspricht. Wenn das Ergebnis der Parametererweiterung nicht leer ist ( ! [ -z ${...} ]), handelt es sich um eine Zahl, da sie kein nichtstelliges Zeichen enthält.
Mrucci
Leider schlägt dies fehl, wenn das Argument Ziffern enthält, auch wenn es sich nicht um eine gültige Nummer handelt. Zum Beispiel "Beispiel1" oder "a2b".
Glenn, ich entferne shopt -s extglobaus Ihrem Beitrag (den ich positiv bewertet habe, das ist eine meiner Lieblingsantworten hier), da Sie in Bedingte Konstrukte lesen können: Wenn die Operatoren ==und !=verwendet werden, wird die Zeichenfolge rechts neben dem Operator als Muster betrachtet und abgeglichen gemäß den unten in Pattern Matching beschriebenen Regeln , als ob die extglobShell-Option aktiviert wäre. Ich hoffe es macht dir nichts aus!
gniourf_gniourf
In solchen Kontexten müssen Sie nicht shopt extglob... das ist gut zu wissen!
gniourf_gniourf
Funktioniert gut für einfache ganze Zahlen.
2
@Jdamian: Sie haben Recht, dies wurde in Bash 4.1 hinzugefügt (das Ende 2009 veröffentlicht wurde… Bash 3.2 wurde 2006 veröffentlicht… es ist jetzt eine antike Software, sorry für diejenigen, die in der Vergangenheit stecken geblieben sind). Sie könnten auch argumentieren, dass Extglobs in Version 2.02 (veröffentlicht 1998) eingeführt wurden und in <2.02-Versionen nicht funktionieren… Jetzt wird Ihr Kommentar hier als Einschränkung in Bezug auf ältere Versionen dienen.
gniourf_gniourf
1
Variablen innerhalb [[...]]unterliegen keiner Wortaufteilung oder Glob-Erweiterung.
Glenn Jackman
26
Ich bin überrascht über die Lösungen, mit denen Zahlenformate direkt in der Shell analysiert werden. Shell ist dafür nicht gut geeignet, da es sich um ein DSL zur Steuerung von Dateien und Prozessen handelt. Etwas weiter unten gibt es reichlich Parser, zum Beispiel:
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 Ihre Version bricht die inhärente (und nützliche) Rückgabewertsemantik der ursprünglichen Funktion. Lassen Sie die Funktion stattdessen einfach isnumber 23 && echo "this is a number" || echo "not a number"
unverändert
4
Sollte das nicht auch 2>/dev/nullso sein, isnumber "foo"damit stderr nicht verschmutzt wird?
Gioele
4
Moderne Shells wie bash als "DSL zur Steuerung von Dateien und Prozessen" zu bezeichnen, bedeutet zu ignorieren, dass sie für viel mehr als das verwendet werden - einige Distributionen haben ganze Paketmanager und Webschnittstellen darauf aufgebaut (so hässlich das auch sein mag). Batch-Dateien passen jedoch zu Ihrer Beschreibung, da selbst das Festlegen einer Variablen dort schwierig ist.
Camilo Martin
7
Es ist lustig, dass Sie versuchen, klug zu sein, indem Sie einige Redewendungen aus anderen Sprachen kopieren. Leider funktioniert dies nicht in Muscheln. Shells sind etwas ganz Besonderes, und ohne fundierte Kenntnisse schreiben Sie wahrscheinlich fehlerhaften Code. Ihr Code ist fehlerhaft: isnumber "'a"wird true zurückgeben. Dies ist in der POSIX-Spezifikation dokumentiert, in der Sie lesen: Wenn das führende Zeichen ein einfaches oder doppeltes Anführungszeichen ist, ist der Wert der numerische Wert im zugrunde liegenden Codesatz des Zeichens nach dem einfachen oder doppelten Anführungszeichen .
gniourf_gniourf
16
Ich habe mir die Antworten angesehen und ... festgestellt, dass niemand an FLOAT-Zahlen (mit Punkt) gedacht hat!
Die Verwendung von grep ist auch großartig.
-E bedeutet erweiterte Regexp
-q bedeutet leise (kein Echo)
-qE ist die Kombination von beiden.
Die Lösungen mit awk von Triple_R und Tripleee arbeiten mit Floats.
Ken Jackson
Danke dafür und sehr guter Punkt! Die Frage ist eigentlich, wie überprüft werden kann, ob es sich um eine Zahl und nicht nur um eine Ganzzahl handelt.
Tanasis
Ich danke dir auch Tanasis! Helfen wir uns immer gegenseitig.
Sergio Abreu
12
Nur eine Fortsetzung von @mary. Aber weil ich nicht genug Repräsentanten habe, konnte ich dies nicht als Kommentar zu diesem Beitrag posten. Wie auch immer, hier ist was ich verwendet habe:
isnum(){ awk -v a="$1"'BEGIN {print (a == a + 0)}';}
Die Funktion gibt "1" zurück, wenn das Argument eine Zahl ist, andernfalls "0". Dies funktioniert sowohl für Ganzzahlen als auch für Gleitkommazahlen. Verwendung ist so etwas wie:
n=-2.05e+07
res=`isnum "$n"`if["$res"=="1"];then
echo "$n is a number"else
echo "$n is not a number"fi
Das Drucken einer Nummer ist weniger nützlich als das Festlegen eines Exit-Codes. 'BEGIN { exit(1-(a==a+0)) }'etwas schwer grok , sondern kann in einer Funktion verwendet werden , die nur wahr oder falsch zurückgibt , wie [, grep -qetc.
tripleee
9
test -z "${i//[0-9]}"&& echo digits || echo no no no
${i//[0-9]}ersetzt eine beliebige Ziffer im Wert von $idurch eine leere Zeichenfolge, siehe man -P 'less +/parameter\/' bash. -zprüft, ob die resultierende Zeichenfolge die Länge Null hat.
Wenn Sie den Fall auch ausschließen möchten, wenn er $ileer ist, können Sie eine der folgenden Konstruktionen verwenden:
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
Daumen hoch speziell für das man -P 'less +/parameter\/' bashTeil. Jeden Tag etwas Neues lernen. :)
David
@sjas Sie können problemlos \-reguläre Ausdrücke hinzufügen , um das Problem zu beheben . Verwenden Sie [0-9\-\.\+]diese Option, um Floats und signierte Nummern zu berücksichtigen.
Für mein Problem musste ich nur sicherstellen, dass ein Benutzer nicht versehentlich Text eingibt, daher habe ich versucht, ihn einfach und lesbar zu halten
isNumber(){(( $1 ))2>/dev/null
}
Laut der Manpage macht das so ziemlich das, was ich will
Wenn der Wert des Ausdrucks nicht Null ist, ist der Rückgabestatus 0
Um böse Fehlermeldungen für Zeichenfolgen zu vermeiden, bei denen es sich möglicherweise um Zahlen handelt, ignoriere ich die Fehlerausgabe
$ ((2s))
bash:((:2s: value too great for base (error token is "2s")
Alte Frage, aber ich wollte nur meine Lösung angehen. Dieser erfordert keine seltsamen Shell-Tricks oder verlässt sich auf etwas, das es nicht für immer gibt.
if[-n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')"];then
echo 'is not numeric'else
echo 'is numeric'fi
Im Grunde werden nur alle Ziffern aus der Eingabe entfernt, und wenn Sie eine Zeichenfolge ungleich Null übrig haben, war dies keine Zahl.
Oder für Variablen mit nachgestellten Zeilenumbrüchen oder ähnlichem $'0\n\n\n1\n\n\n2\n\n\n3\n'.
gniourf_gniourf
7
Ich kann noch keinen Kommentar abgeben, daher füge ich meine eigene Antwort hinzu, die eine Erweiterung von Glenn Jackmans Antwort mit Bash Pattern Matching darstellt.
Mein ursprüngliches Bedürfnis war es, Zahlen zu identifizieren und ganze Zahlen und Gleitkommazahlen zu unterscheiden. Die Funktionsdefinitionen wurden abgezogen von:
function isInteger(){[[ ${1}==?(-)+([0-9])]]}function isFloat(){[[ ${1}==?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9]))]]}
Ich habe Unit-Tests (mit shUnit2) verwendet, um zu überprüfen, ob meine Muster wie beabsichtigt funktionieren:
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}
Hinweise: Das isFloat-Muster kann so geändert werden, dass es gegenüber Dezimalpunkt ( @(.,)) und dem E-Symbol ( @(Ee)) toleranter ist . Meine Unit-Tests testen nur Werte, die entweder ganzzahlig oder float sind, aber keine ungültigen Eingaben.
Entweder Duplikat oder vielleicht besser als Kommentar zu Pixelbeats Antwort geeignet (Verwendung %fist wahrscheinlich sowieso besser)
Michael
3
Warum nicht einfach den vorherigen Statuscode überprüfen, sondern ihn ifselbst eingeben ? Das ist was ifmacht ...if printf "%g" "$var" &> /dev/null; then ...
Camilo Martin
3
Dies hat andere Einschränkungen. Es überprüft die leere Zeichenfolge und Zeichenfolgen wie 'a.
gniourf_gniourf
6
Eine klare Antwort wurde bereits von @charles Dufy und anderen gegeben. Eine reine Bash-Lösung würde Folgendes verwenden:
string="-12,345"if[["$string"=~^-?[0-9]+[.,]?[0-9]*$ ]]then
echo $string is a number
else
echo $string is not a number
fi
Obwohl es für reelle Zahlen nicht zwingend erforderlich ist, eine Zahl vor dem Radixpunkt zu haben .
Um die schwebenden Zahlen und die wissenschaftliche Notation gründlicher zu unterstützen (viele Programme in C / Fortran oder exportieren Float auf diese Weise), wäre eine nützliche Ergänzung zu dieser Zeile die folgende:
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
Dies führt zu einer Möglichkeit, Zahlentypen zu unterscheiden, wenn Sie nach einem bestimmten Typ suchen:
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
Hinweis: Wir könnten die syntaktischen Anforderungen für die Dezimal- und wissenschaftliche Notation auflisten. Eine davon besteht darin, Komma als Radixpunkt sowie "." Zuzulassen. Wir würden dann behaupten, dass es nur einen solchen Radixpunkt geben muss. In einem [Ee] -Float können zwei +/- Zeichen stehen. Ich habe ein paar weitere Regeln aus Aulus Arbeit gelernt und gegen schlechte Saiten wie '' '-' '-E-1' '0-0' getestet. Hier sind meine Regex / Teilzeichenfolge / Ausdruck-Tools, die anscheinend halten:
Was ist die Mindestversion von Bash? Ich bekomme nur bash: bedingter binärer Operator erwartet bash: Syntaxfehler in der Nähe eines unerwarteten Tokens `= ~ '
Paul Hargreaves
1
@PaulHargreaves =~existierte mindestens schon in Bash 3.0.
Gilles 'SO - hör auf böse zu sein'
@ PaulHargreaves Sie hatten wahrscheinlich ein Problem mit Ihrem ersten Operanden, zB zu viele Anführungszeichen oder ähnliches
Joshua Clayton
@ JoshuaClayton Ich habe nach der Version gefragt, weil es eine sehr sehr alte Bash auf einer Solaris 7-Box ist, die wir noch haben und die nicht unterstützt wird = ~
Paul Hargreaves
6
Dies kann erreicht werden, indem überprüft wird grep, ob die betreffende Variable mit einem erweiterten regulären Ausdruck übereinstimmt.
Test Ganzzahl 1120:
yournumber=1120if echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Ausgabe: Valid number.
Test nicht ganzzahlig 1120a:
yournumber=1120aif echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Ausgabe: Error: not a number.
Erläuterung
Mit grepdem -ESchalter können wir erweiterte reguläre Ausdrücke verwenden '^[0-9]+$'. Dieser reguläre Ausdruck bedeutet, dass die Variable vom Anfang bis zum Ende der Variablen nur []die Zahlen 0-9Null bis Neun enthalten und mindestens ein Zeichen haben sollte.^$+
Die grep, die-q ruhigen Schalter ausschaltet jeden Ausgang , ob es etwas findet.
ifprüft den Exit-Status von grep. Status beenden0 bedeutet Erfolg und alles, was größer ist, bedeutet einen Fehler. Der grepBefehl hat den Beendigungsstatus, 0ob eine Übereinstimmung gefunden wird und 1wann nicht.
Wenn ifwir also alles zusammensetzen, geben wir im Test echodie Variable an $yournumberund |leiten sie weiter, grepmit der der -qSchalter stillschweigend mit dem -Eerweiterten Ausdruck des regulären Ausdrucks übereinstimmt '^[0-9]+$'. Der Exit-Status von grepwird angezeigt, 0wenn grepeine Übereinstimmung erfolgreich gefunden wurde und 1wenn dies nicht der Fall war . Wenn es gelingt, zu passen, wir echo "Valid number.". Wenn es nicht passt, wir echo "Error: not a number.".
Für Schwimmer oder Doppel
Wir können einfach den regulären Ausdruck von '^[0-9]+$'bis ändern'^[0-9]*\.?[0-9]+$' für Floats oder Doubles .
Testschwimmer 1120.01:
yournumber=1120.01if echo "$yournumber"| grep -qE '^[0-9]*\.?[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Ausgabe: Valid number.
Testschwimmer 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
Ausgabe: Error: not a number.
Für Negative
Um negative Ganzzahlen zuzulassen, ändern Sie einfach den regulären Ausdruck von '^[0-9]+$'nach '^\-?[0-9]+$'.
Um negative Floats oder Doubles zuzulassen, ändern Sie einfach den regulären Ausdruck von '^[0-9]*\.?[0-9]+$'nach '^\-?[0-9]*\.?[0-9]+$'.
Du hast recht, @CharlesDuffy. Vielen Dank. Ich habe die Antwort aufgeräumt.
Joseph Shih
Richtig, @CharlesDuffy. Fest!
Joseph Shih
1
LGTM; Antwort wie bearbeitet hat meine +1. Die einzigen Dinge, die ich an dieser Stelle anders machen würde, sind eher Meinungsfragen als Korrektheit (f / e, [-]anstelle von \-und [.]anstelle von \.ist etwas ausführlicher, aber es bedeutet, dass sich Ihre Zeichenfolgen nicht ändern müssen, wenn sie ' werden in einem Kontext verwendet, in dem Backslashes verbraucht werden).
Sie können auch die Zeichenklassen von bash verwenden.
if[[ $VAR =*[[:digit:]]*]];then
echo "$VAR is numeric"else
echo "$VAR is not numeric"fi
Numerik enthält Leerzeichen, den Dezimalpunkt und "e" oder "E" für Gleitkommazahlen.
Wenn Sie jedoch eine Hexadezimalzahl im C-Stil angeben, dh "0xffff" oder "0XFFFF", gibt [[: digit:]] true zurück. Bash ist hier eine Art Falle und ermöglicht es Ihnen, etwas wie "0xAZ00" zu tun und es trotzdem als Ziffer zu zählen (ist dies nicht eine seltsame Eigenart von GCC-Compilern, mit denen Sie die 0x-Notation für andere Basen als 16 verwenden können ??? )
Möglicherweise möchten Sie vor dem Testen auf "0x" oder "0X" testen, ob es sich um eine Zahl handelt, wenn Ihre Eingabe nicht vertrauenswürdig ist, es sei denn, Sie möchten Hex-Zahlen akzeptieren. Dies würde erreicht werden durch:
if[[ ${VARIABLE:1:2}="0x"]]||[[ ${VARIABLE:1:2}="0X"]];then echo "$VAR is not numeric";fi
@ultraswadable, Ihre Lösung erkennt Zeichenfolgen, die mindestens eine Ziffer enthalten, die von anderen Zeichen umgeben ist (oder nicht). Ich habe abgelehnt.
Jdamian
Der offensichtlich richtige Ansatz ist daher, dies umzukehren und[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
eschwartz
@eschwartz, Ihre Lösung funktioniert nicht mit negativen Zahlen
Angel
4
Am einfachsten ist es zu überprüfen, ob es nichtstellige Zeichen enthält. Sie ersetzen alle Ziffern durch nichts und prüfen die Länge. Wenn es Länge gibt, ist es keine Zahl.
if[[!-n ${input//[0-9]/}]];then
echo "Input Is A Number"fi
Der Umgang mit negativen Zahlen würde einen komplizierteren Ansatz erfordern.
Andy
... oder ein optionales positives Vorzeichen.
Tripleee
@tripleee Ich würde gerne Ihren Ansatz sehen, wenn Sie wissen, wie es geht.
Andy
4
Da musste ich in letzter Zeit daran manipulieren und wie Karttus Ansatz mit dem Unit-Test am meisten. Ich habe den Code überarbeitet und einige andere Lösungen hinzugefügt. Probieren Sie es selbst aus, um die Ergebnisse zu sehen:
So ISNUMBER () enthält Striche, Kommata und Exponentialnotation und daher liefert TRUE auf ganze Zahlen und Schwimmern , wo auf der anderen Seite isFloat () kehrt FALSE auf ganzzahlige Werte und isInteger () ebenfalls FALSCH auf Schwimmern zurückkehrt. Für Ihre Bequemlichkeit alle als ein Liner:
Persönlich würde ich das functionSchlüsselwort entfernen, da es nichts Nützliches tut. Ich bin mir auch nicht sicher, ob die Rückgabewerte nützlich sind. Sofern nicht anders angegeben, geben die Funktionen den Exit-Status des letzten Befehls zurück, sodass Sie selbst nichts benötigen return.
Tom Fenech
Schön, in der Tat sind die returns verwirrend und machen es weniger lesbar. Das Verwenden von functionSchlüsselwörtern oder nicht ist eher eine Frage des persönlichen Geschmacks, zumindest habe ich sie aus den Einzeilern entfernt, um Platz zu sparen. Danke.
3ronco
Vergessen Sie nicht, dass nach den Tests für die einzeiligen Versionen Semikolons benötigt werden.
Tom Fenech
2
isNumber gibt 'true' für jede Zeichenfolge zurück, die eine Nummer enthält.
DrStrangepork
@DrStrangepork In der Tat fehlt meinem Array false_values dieser Fall. Ich werde mich darum kümmern. Danke für den Tipp.
3ronco
4
Ich benutze expr . Es wird eine Nicht-Null zurückgegeben, wenn Sie versuchen, einem nicht numerischen Wert eine Null hinzuzufügen:
if expr --"$number"+0>/dev/null 2>&1then
echo "$number is a number"else
echo "$number isn't a number"fi
Es könnte möglich sein, bc zu verwenden, wenn Sie Nicht-Ganzzahlen benötigen, aber ich glaube nicht, dass bces das gleiche Verhalten hat. Wenn Sie einer Nichtzahl Null hinzufügen, erhalten Sie Null und es wird auch der Wert Null zurückgegeben. Vielleicht kannst du bcund kombinieren expr. Verwenden Sie bcdiese Option , um Null zu addieren $number. Wenn die Antwort lautet 0, versuchen Sie exprzu überprüfen, ob diese $numbernicht Null ist.
Das ist ziemlich schlecht. Um es etwas besser zu machen, sollten Sie verwenden expr -- "$number" + 0; dennoch wird dies immer noch so tun 0 isn't a number. Von man expr:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
gniourf_gniourf
3
Ich mag die Antwort von Alberto Zaccagni.
if["$var"-eq "$var"]2>/dev/null;then
Wichtige Voraussetzungen: - Keine Subshells erzeugt - Keine RE-Parser aufgerufen - Die meisten Shell-Anwendungen verwenden keine reellen Zahlen
Aber wenn dies $varkomplex ist (z. B. ein assoziativer Array-Zugriff) und die Zahl eine nicht negative Ganzzahl ist (die meisten Anwendungsfälle), ist dies möglicherweise effizienter?
Ich verwende printf als andere erwähnte Antworten. Wenn Sie die Formatzeichenfolge "% f" oder "% i" angeben, übernimmt printf die Überprüfung für Sie. Die Syntax ist einfacher als die Neuerfindung der Prüfungen. Sie ist einfach und kurz und printf ist allgegenwärtig. Meiner Meinung nach ist es eine gute Wahl - Sie können auch die folgende Idee verwenden, um nach einer Reihe von Dingen zu suchen, es ist nicht nur nützlich, um Zahlen zu überprüfen.
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; }
Als Antwort von Charles Duffy funktioniert, aber nur verwendenBash regulären Ausdruck , und ich weiß, dass dies langsam ist, ich möchte einen anderen Weg zeigen, nur mitBashParametererweiterung :
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
OK, das ist gut. Nun, wie viel Zeit wird das alles dauern (auf meinem Himbeer-Pi):
time for i in{1..1000};do is_num +3.14159265;done
real 0m2.476s
user 0m1.235s
sys 0m0.000s
Dann mit regulären Ausdrücken:
time for i in{1..1000};do cdIs_num +3.14159265;done
real 0m4.363s
user 0m2.168s
sys 0m0.000s
Ich stimme sowieso zu, ich bevorzuge es, keinen regulären Ausdruck zu verwenden, wenn ich die Parametererweiterung verwenden könnte ... Der Missbrauch von RE verlangsamt das Bash-Skript
F. Hauri
Antwort bearbeitet: Keine Notwendigkeit extglob(und etwas schneller)!
F. Hauri
@CharlesDuffy, Auf meinem Himbeer-Pi dauert die Iteration von 1000 mit meiner Version 2,5 Sekunden und mit Ihrer Version 4,4 Sekunden!
F. Hauri
Kann damit nicht streiten. 👍
Charles Duffy
1
Um negative Zahlen zu fangen:
if[[ $1 ==?(-)+([0-9.])]]then
echo number
else
echo not a number
fi
Dies erfordert auch, dass zuerst erweitertes Globbing aktiviert wird. Dies ist eine Nur-Bash-Funktion, die standardmäßig deaktiviert ist.
Tripleee
@tripleee Extended Globbing wird automatisch aktiviert, wenn == oder! = 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 Danke für das Update. Dies scheint ein neues Verhalten zu sein, das in meinem Bash 3.2.57 (MacOS Mojave) nicht zutrifft. Ich sehe, dass es so funktioniert, wie Sie es in 4.4 beschrieben haben.
Tripleee
1
Sie könnten "let" auch so verwenden:
[~]$ 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
[~]$
Aber ich bevorzuge es, den Operator "= ~" Bash 3+ zu verwenden, wie einige Antworten in diesem Thread.
Downvote wegen mangelnder Erklärung. Wie funktioniert das? Es sieht komplex und spröde aus und es ist nicht offensichtlich, welche Eingaben genau akzeptiert werden. (Ist das Entfernen von Leerzeichen beispielsweise von entscheidender Bedeutung? Warum? Es wird angegeben, dass eine Zahl mit eingebetteten Leerzeichen eine gültige Zahl ist, was möglicherweise nicht wünschenswert ist.)
Tripleee
1
Habe hier dasselbe mit einem regulären Ausdruck gemacht, der den gesamten Teil und den Dezimalteil testet, getrennt durch einen Punkt.
Können Sie erklären, warum sich Ihre Antwort grundlegend von anderen alten Antworten unterscheidet, z. B. Charles Duffys Antwort? Nun, Ihre Antwort ist tatsächlich gebrochen, da sie eine einzelne Periode bestätigt.
gniourf_gniourf
Ich bin mir nicht sicher, ob ich die einzelne Periode hier verstehen soll ... es wird eine oder eine Nullperiode erwartet ... Aber richtig, nichts grundlegend anderes, ich fand den regulären Ausdruck einfach leichter zu lesen.
Jerome
auch mit * sollte mehr realen Fällen entsprechen
Jerome
Die Sache ist, dass Sie die leere Zeichenfolge a=''und die Zeichenfolge, die nur einen Punkt enthält, abgleichen, a='.'damit Ihr Code ein bisschen kaputt ist ...
gniourf_gniourf
0
Ich benutze Folgendes (für ganze Zahlen):
## ##### 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
Fast richtig (Sie akzeptieren die leere Zeichenfolge), aber dankbar kompliziert bis zur Verschleierung.
Gilles 'SO - hör auf böse zu sein'
2
Falsch: Sie akzeptieren -nusw. (wegen echo) und Sie akzeptieren Variablen mit nachgestellten Zeilenumbrüchen (wegen $(...)). Und übrigens printist kein gültiger Shell-Befehl.
gniourf_gniourf
0
Ich habe das Rezept von Ultrasawblade ausprobiert, da es mir am praktischsten erschien und es nicht zum Laufen bringen konnte. Am Ende habe ich mir jedoch einen anderen Weg ausgedacht, der wie andere auf der Parametersubstitution basiert, diesmal mit Regex-Ersetzung:
[["${var//*([[:digit:]])}"]];&& echo "$var is not numeric"|| echo "$var is numeric"
Es entfernt jedes: digit: class-Zeichen in $ var und prüft, ob eine leere Zeichenfolge übrig bleibt, was bedeutet, dass das Original nur aus Zahlen besteht.
Was ich an diesem Modell mag, ist sein geringer Platzbedarf und seine Flexibilität. In dieser Form funktioniert es nur für nicht begrenzte Ganzzahlen der Basis 10, obwohl Sie sicherlich den Mustervergleich verwenden können, um ihn an andere Anforderungen anzupassen.
Wenn man die Lösung von mrucci liest, sieht sie fast genauso aus wie meine, verwendet jedoch einen regulären Saitenersatz anstelle von "sed style". Beide verwenden dieselben Regeln für den Mustervergleich und sind, AFAIK, austauschbare Lösungen.
ata
sedist POSIX, während Ihre Lösung ist bash. Beide haben ihre Verwendung
v010dya
0
Quick & Dirty: Ich weiß, dass dies nicht die eleganteste Art ist, aber ich habe normalerweise nur eine Null hinzugefügt und das Ergebnis getestet. wie so:
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)) gibt 0 oder bomb zurück, wenn $ 1 KEINE ganze Zahl ist. zum Beispiel:
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
}
HINWEIS: Ich denke, ich hätte nie gedacht, nach Floats oder gemischten Typen zu suchen, die die gesamte Skriptbombe erzeugen ... in meinem Fall wollte ich nicht, dass es weiter geht. Ich werde mit Mruccis Lösung und Duffys Regex herumspielen - sie scheinen die robustesten im Bash-Framework zu sein ...
Dies akzeptiert arithmetische Ausdrücke wie 1+1, lehnt jedoch einige positive ganze Zahlen mit führenden 0s ab (da 08es sich um eine ungültige Oktalkonstante handelt).
Gilles 'SO - hör auf böse zu sein'
2
Dies hat auch andere Probleme: 0Ist keine Zahl und unterliegt einer willkürlichen Code-Injektion. Probieren Sie es aus : isInteger 'a[$(ls)]'. Hoppla.
gniourf_gniourf
1
Und die Erweiterung von $((...))ist nicht zitiert, eine Zahl IFS=123wird es ändern.
test && echo "foo" && exit 0 || echo "bar" && exit 1
Ihnen verwendete Ansatz kann einige unbeabsichtigte Nebenwirkungen haben - wird das Echoexit 0
übersprungen (möglicherweise wird es an einen geschlossenen FD ausgegeben), das wird übersprungen, und der Code versucht es dannecho "bar"
. Wenn dies auch fehlschlägt, schlägt die&&
Bedingung fehl und wird nicht einmal ausgeführtexit 1
! Die Verwendung tatsächlicherif
Aussagen anstelle von&&
/||
ist weniger anfällig für unerwartete Nebenwirkungen.[[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }
Die geschweiften Klammern zeigen an, dass Dinge NICHT in einer Unterschale ausgeführt werden sollten (was definitiv so wäre, wenn()
stattdessen Klammern verwendet würden). Vorsichtsmaßnahme: Verpassen Sie niemals das letzte Semikolon . Andernfalls könnten Siebash
die hässlichsten (und sinnlosesten) Fehlermeldungen[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Antworten:
Ein Ansatz besteht darin, einen regulären Ausdruck wie folgt zu verwenden:
Wenn der Wert nicht unbedingt eine Ganzzahl ist, sollten Sie die Regex entsprechend ändern. zum Beispiel:
... oder um mit Zahlen mit einem Zeichen umzugehen:
quelle
^-?
eher machen, als^-*
wenn Sie nicht tatsächlich die Arbeit machen, um mehrere Inversionen richtig zu handhaben.[[ $yournumber =~ ^[0-9]+$ ]]
.Ohne Bashismen (funktioniert sogar im System V sh),
Dies lehnt leere Zeichenfolgen und Zeichenfolgen mit nicht-Ziffern ab und akzeptiert alles andere.
Negative oder Gleitkommazahlen erfordern zusätzliche Arbeit. Eine Idee ist,
-
/.
im ersten "schlechten" Muster auszuschließen und weitere "schlechte" Muster hinzuzufügen, die deren unangemessene Verwendung enthalten (?*-*
/*.*.*
)quelle
if test ...
${string#-}
(was in antiken Bourne-Shells nicht funktioniert, aber in jeder POSIX-Shell funktioniert) ändern , um negative Ganzzahlen zu akzeptieren.'.' | *.*.*
die nicht zulässigen Muster hinzu und fügen Sie den zulässigen Zeichen einen Punkt hinzu. Ebenso können Sie vorher ein optionales Zeichen zulassen, obwohl ich es dann vorziehen würdecase ${string#[-+]}
, das Zeichen einfach zu ignorieren.Die folgende Lösung kann auch in Basis-Shells wie Bourne verwendet werden, ohne dass reguläre Ausdrücke erforderlich sind. Grundsätzlich führen alle numerischen Wertevaluierungsoperationen, bei denen keine Zahlen verwendet werden, zu einem Fehler, der in der Shell implizit als falsch betrachtet wird:
wie in:
Sie können auch für $ testen? der Rückkehrcode der Operation, der expliziter ist:
Die Umleitung des Standardfehlers dient dazu, die Meldung "Ganzzahliger Ausdruck erwartet" auszublenden, die von bash gedruckt wird, falls wir keine Nummer haben.
CAVEATS (dank der Kommentare unten):
[[ ]]
statt[ ]
wird immer ausgewertettrue
true
bash: [[: 1 a: syntax error in expression (error token is "a")
bash: [[: i: expression recursion level exceeded (error token is "i")
quelle
[[ a -eq a ]]
wird als wahr ausgewertet (beide Argumente werden in Null umgewandelt)if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
[
Einbau die Argumente als arithmetisch bewertet, nicht unerheblich ist . Das gilt sowohl für ksh93 als auch für mksh. Da diese beiden Arrays unterstützt werden, besteht außerdem eine einfache Möglichkeit zur Code-Injektion. Verwenden Sie stattdessen eine Musterübereinstimmung.[[ ]]
aber nicht für numerische Kontextregeln interpretiert[ ]
. Dieses Verhalten ist jedoch sowohl im POSIX-Standard fürtest
als auch in der eigenen Dokumentation von bash nicht spezifiziert . Zukünftige Versionen von bash könnten das Verhalten so ändern, dass es mit ksh übereinstimmt, ohne dokumentierte Verhaltensversprechen zu brechen. Daher ist es nicht garantiert, dass es sicher ist, sich auf das derzeitige Verhalten zu verlassen.Dies testet, ob eine Zahl eine nicht negative Ganzzahl ist und sowohl Shell-unabhängig (dh ohne Bashismen) als auch nur Shell-integrierte Funktionen verwendet:
FALSCH.
Da diese erste Antwort (unten) Ganzzahlen mit Zeichen zulässt, solange die ersten nicht die ersten in der Variablen sind.
RICHTIG .
Wie Jilles in seiner Antwort kommentierte und vorschlug , ist dies der richtige Weg, dies mit Shell-Mustern zu tun.
quelle
*[!0-9]*
ist ein Muster, das allen Zeichenfolgen mit mindestens einem nichtstelligen Zeichen entspricht.${num##*[!0-9]*}
ist eine "Parametererweiterung", bei der wir den Inhalt dernum
Variablen nehmen und die längste Zeichenfolge entfernen, die dem Muster entspricht. Wenn das Ergebnis der Parametererweiterung nicht leer ist (! [ -z ${...} ]
), handelt es sich um eine Zahl, da sie kein nichtstelliges Zeichen enthält.122s
:-(
Niemand schlug Bashs erweiterten Mustervergleich vor :
quelle
shopt -s extglob
aus Ihrem Beitrag (den ich positiv bewertet habe, das ist eine meiner Lieblingsantworten hier), da Sie in Bedingte Konstrukte lesen können: Wenn die Operatoren==
und!=
verwendet werden, wird die Zeichenfolge rechts neben dem Operator als Muster betrachtet und abgeglichen gemäß den unten in Pattern Matching beschriebenen Regeln , als ob dieextglob
Shell-Option aktiviert wäre. Ich hoffe es macht dir nichts aus!shopt extglob
... das ist gut zu wissen![[...]]
unterliegen keiner Wortaufteilung oder Glob-Erweiterung.Ich bin überrascht über die Lösungen, mit denen Zahlenformate direkt in der Shell analysiert werden. Shell ist dafür nicht gut geeignet, da es sich um ein DSL zur Steuerung von Dateien und Prozessen handelt. Etwas weiter unten gibt es reichlich Parser, zum Beispiel:
Ändern Sie '% f' in das gewünschte Format.
quelle
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
so sein,isnumber "foo"
damit stderr nicht verschmutzt wird?isnumber "'a"
wird true zurückgeben. Dies ist in der POSIX-Spezifikation dokumentiert, in der Sie lesen: Wenn das führende Zeichen ein einfaches oder doppeltes Anführungszeichen ist, ist der Wert der numerische Wert im zugrunde liegenden Codesatz des Zeichens nach dem einfachen oder doppelten Anführungszeichen .Ich habe mir die Antworten angesehen und ... festgestellt, dass niemand an FLOAT-Zahlen (mit Punkt) gedacht hat!
Die Verwendung von grep ist auch großartig.
-E bedeutet erweiterte Regexp
-q bedeutet leise (kein Echo)
-qE ist die Kombination von beiden.
So testen Sie direkt in der Befehlszeile:
Verwenden in einem Bash-Skript:
Verwenden Sie Folgendes, um NUR Ganzzahlen abzugleichen:
quelle
Nur eine Fortsetzung von @mary. Aber weil ich nicht genug Repräsentanten habe, konnte ich dies nicht als Kommentar zu diesem Beitrag posten. Wie auch immer, hier ist was ich verwendet habe:
Die Funktion gibt "1" zurück, wenn das Argument eine Zahl ist, andernfalls "0". Dies funktioniert sowohl für Ganzzahlen als auch für Gleitkommazahlen. Verwendung ist so etwas wie:
quelle
'BEGIN { exit(1-(a==a+0)) }'
etwas schwer grok , sondern kann in einer Funktion verwendet werden , die nur wahr oder falsch zurückgibt , wie[
,grep -q
etc.${i//[0-9]}
ersetzt eine beliebige Ziffer im Wert von$i
durch eine leere Zeichenfolge, sieheman -P 'less +/parameter\/' bash
.-z
prüft, ob die resultierende Zeichenfolge die Länge Null hat.Wenn Sie den Fall auch ausschließen möchten, wenn er
$i
leer ist, können Sie eine der folgenden Konstruktionen verwenden:quelle
man -P 'less +/parameter\/' bash
Teil. Jeden Tag etwas Neues lernen. :)\-
reguläre Ausdrücke hinzufügen , um das Problem zu beheben . Verwenden Sie[0-9\-\.\+]
diese Option, um Floats und signierte Nummern zu berücksichtigen.echo $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
Für mein Problem musste ich nur sicherstellen, dass ein Benutzer nicht versehentlich Text eingibt, daher habe ich versucht, ihn einfach und lesbar zu halten
Laut der Manpage macht das so ziemlich das, was ich will
Um böse Fehlermeldungen für Zeichenfolgen zu vermeiden, bei denen es sich möglicherweise um Zahlen handelt, ignoriere ich die Fehlerausgabe
quelle
Alte Frage, aber ich wollte nur meine Lösung angehen. Dieser erfordert keine seltsamen Shell-Tricks oder verlässt sich auf etwas, das es nicht für immer gibt.
Im Grunde werden nur alle Ziffern aus der Eingabe entfernt, und wenn Sie eine Zeichenfolge ungleich Null übrig haben, war dies keine Zahl.
quelle
var
.$'0\n\n\n1\n\n\n2\n\n\n3\n'
.Ich kann noch keinen Kommentar abgeben, daher füge ich meine eigene Antwort hinzu, die eine Erweiterung von Glenn Jackmans Antwort mit Bash Pattern Matching darstellt.
Mein ursprüngliches Bedürfnis war es, Zahlen zu identifizieren und ganze Zahlen und Gleitkommazahlen zu unterscheiden. Die Funktionsdefinitionen wurden abgezogen von:
Ich habe Unit-Tests (mit shUnit2) verwendet, um zu überprüfen, ob meine Muster wie beabsichtigt funktionieren:
Hinweise: Das isFloat-Muster kann so geändert werden, dass es gegenüber Dezimalpunkt (
@(.,)
) und dem E-Symbol (@(Ee)
) toleranter ist . Meine Unit-Tests testen nur Werte, die entweder ganzzahlig oder float sind, aber keine ungültigen Eingaben.quelle
Ich würde das versuchen:
Hinweis: Dies erkennt nan und inf als Zahl.
quelle
%f
ist wahrscheinlich sowieso besser)if
selbst eingeben ? Das ist wasif
macht ...if printf "%g" "$var" &> /dev/null; then ...
'a
.Eine klare Antwort wurde bereits von @charles Dufy und anderen gegeben. Eine reine Bash-Lösung würde Folgendes verwenden:
Obwohl es für reelle Zahlen nicht zwingend erforderlich ist, eine Zahl vor dem Radixpunkt zu haben .
Um die schwebenden Zahlen und die wissenschaftliche Notation gründlicher zu unterstützen (viele Programme in C / Fortran oder exportieren Float auf diese Weise), wäre eine nützliche Ergänzung zu dieser Zeile die folgende:
Dies führt zu einer Möglichkeit, Zahlentypen zu unterscheiden, wenn Sie nach einem bestimmten Typ suchen:
Hinweis: Wir könnten die syntaktischen Anforderungen für die Dezimal- und wissenschaftliche Notation auflisten. Eine davon besteht darin, Komma als Radixpunkt sowie "." Zuzulassen. Wir würden dann behaupten, dass es nur einen solchen Radixpunkt geben muss. In einem [Ee] -Float können zwei +/- Zeichen stehen. Ich habe ein paar weitere Regeln aus Aulus Arbeit gelernt und gegen schlechte Saiten wie '' '-' '-E-1' '0-0' getestet. Hier sind meine Regex / Teilzeichenfolge / Ausdruck-Tools, die anscheinend halten:
quelle
Vergessen Sie nicht,
-
negative Zahlen anzugeben!quelle
=~
existierte mindestens schon in Bash 3.0.Dies kann erreicht werden, indem überprüft wird
grep
, ob die betreffende Variable mit einem erweiterten regulären Ausdruck übereinstimmt.Test Ganzzahl
1120
:Ausgabe:
Valid number.
Test nicht ganzzahlig
1120a
:Ausgabe:
Error: not a number.
Erläuterung
grep
dem-E
Schalter können wir erweiterte reguläre Ausdrücke verwenden'^[0-9]+$'
. Dieser reguläre Ausdruck bedeutet, dass die Variable vom Anfang bis zum Ende der Variablen nur[]
die Zahlen0-9
Null bis Neun enthalten und mindestens ein Zeichen haben sollte.^
$
+
grep
, die-q
ruhigen Schalter ausschaltet jeden Ausgang , ob es etwas findet.if
prüft den Exit-Status vongrep
. Status beenden0
bedeutet Erfolg und alles, was größer ist, bedeutet einen Fehler. Dergrep
Befehl hat den Beendigungsstatus,0
ob eine Übereinstimmung gefunden wird und1
wann nicht.Wenn
if
wir also alles zusammensetzen, geben wir im Testecho
die Variable an$yournumber
und|
leiten sie weiter,grep
mit der der-q
Schalter stillschweigend mit dem-E
erweiterten Ausdruck des regulären Ausdrucks übereinstimmt'^[0-9]+$'
. Der Exit-Status vongrep
wird angezeigt,0
wenngrep
eine Übereinstimmung erfolgreich gefunden wurde und1
wenn dies nicht der Fall war . Wenn es gelingt, zu passen, wirecho "Valid number."
. Wenn es nicht passt, wirecho "Error: not a number."
.Für Schwimmer oder Doppel
Wir können einfach den regulären Ausdruck von
'^[0-9]+$'
bis ändern'^[0-9]*\.?[0-9]+$'
für Floats oder Doubles .Testschwimmer
1120.01
:Ausgabe:
Valid number.
Testschwimmer
11.20.01
:Ausgabe:
Error: not a number.
Für Negative
Um negative Ganzzahlen zuzulassen, ändern Sie einfach den regulären Ausdruck von
'^[0-9]+$'
nach'^\-?[0-9]+$'
.Um negative Floats oder Doubles zuzulassen, ändern Sie einfach den regulären Ausdruck von
'^[0-9]*\.?[0-9]+$'
nach'^\-?[0-9]*\.?[0-9]+$'
.quelle
[-]
anstelle von\-
und[.]
anstelle von\.
ist etwas ausführlicher, aber es bedeutet, dass sich Ihre Zeichenfolgen nicht ändern müssen, wenn sie ' werden in einem Kontext verwendet, in dem Backslashes verbraucht werden).http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
Sie können auch die Zeichenklassen von bash verwenden.
Numerik enthält Leerzeichen, den Dezimalpunkt und "e" oder "E" für Gleitkommazahlen.
Wenn Sie jedoch eine Hexadezimalzahl im C-Stil angeben, dh "0xffff" oder "0XFFFF", gibt [[: digit:]] true zurück. Bash ist hier eine Art Falle und ermöglicht es Ihnen, etwas wie "0xAZ00" zu tun und es trotzdem als Ziffer zu zählen (ist dies nicht eine seltsame Eigenart von GCC-Compilern, mit denen Sie die 0x-Notation für andere Basen als 16 verwenden können ??? )
Möglicherweise möchten Sie vor dem Testen auf "0x" oder "0X" testen, ob es sich um eine Zahl handelt, wenn Ihre Eingabe nicht vertrauenswürdig ist, es sei denn, Sie möchten Hex-Zahlen akzeptieren. Dies würde erreicht werden durch:
quelle
[[ $VAR = *[[:digit:]]* ]]
gibt true zurück, wenn die Variable eine Zahl enthält , nicht wenn es sich um eine Ganzzahl handelt.[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"
drucktnumeric
. In Bash-Version getestet3.2.25(1)-release
.[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
Am einfachsten ist es zu überprüfen, ob es nichtstellige Zeichen enthält. Sie ersetzen alle Ziffern durch nichts und prüfen die Länge. Wenn es Länge gibt, ist es keine Zahl.
quelle
Da musste ich in letzter Zeit daran manipulieren und wie Karttus Ansatz mit dem Unit-Test am meisten. Ich habe den Code überarbeitet und einige andere Lösungen hinzugefügt. Probieren Sie es selbst aus, um die Ergebnisse zu sehen:
So ISNUMBER () enthält Striche, Kommata und Exponentialnotation und daher liefert TRUE auf ganze Zahlen und Schwimmern , wo auf der anderen Seite isFloat () kehrt FALSE auf ganzzahlige Werte und isInteger () ebenfalls FALSCH auf Schwimmern zurückkehrt. Für Ihre Bequemlichkeit alle als ein Liner:
quelle
function
Schlüsselwort entfernen, da es nichts Nützliches tut. Ich bin mir auch nicht sicher, ob die Rückgabewerte nützlich sind. Sofern nicht anders angegeben, geben die Funktionen den Exit-Status des letzten Befehls zurück, sodass Sie selbst nichts benötigenreturn
.return
s verwirrend und machen es weniger lesbar. Das Verwenden vonfunction
Schlüsselwörtern oder nicht ist eher eine Frage des persönlichen Geschmacks, zumindest habe ich sie aus den Einzeilern entfernt, um Platz zu sparen. Danke.Ich benutze expr . Es wird eine Nicht-Null zurückgegeben, wenn Sie versuchen, einem nicht numerischen Wert eine Null hinzuzufügen:
Es könnte möglich sein, bc zu verwenden, wenn Sie Nicht-Ganzzahlen benötigen, aber ich glaube nicht, dass
bc
es das gleiche Verhalten hat. Wenn Sie einer Nichtzahl Null hinzufügen, erhalten Sie Null und es wird auch der Wert Null zurückgegeben. Vielleicht kannst dubc
und kombinierenexpr
. Verwenden Siebc
diese Option , um Null zu addieren$number
. Wenn die Antwort lautet0
, versuchen Sieexpr
zu überprüfen, ob diese$number
nicht Null ist.quelle
expr -- "$number" + 0
; dennoch wird dies immer noch so tun0 isn't a number
. Vonman expr
:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
Ich mag die Antwort von Alberto Zaccagni.
Wichtige Voraussetzungen: - Keine Subshells erzeugt - Keine RE-Parser aufgerufen - Die meisten Shell-Anwendungen verwenden keine reellen Zahlen
Aber wenn dies
$var
komplex ist (z. B. ein assoziativer Array-Zugriff) und die Zahl eine nicht negative Ganzzahl ist (die meisten Anwendungsfälle), ist dies möglicherweise effizienter?quelle
Ich verwende printf als andere erwähnte Antworten. Wenn Sie die Formatzeichenfolge "% f" oder "% i" angeben, übernimmt printf die Überprüfung für Sie. Die Syntax ist einfacher als die Neuerfindung der Prüfungen. Sie ist einfach und kurz und printf ist allgegenwärtig. Meiner Meinung nach ist es eine gute Wahl - Sie können auch die folgende Idee verwenden, um nach einer Reihe von Dingen zu suchen, es ist nicht nur nützlich, um Zahlen zu überprüfen.
>$ var=45
>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }
quelle
Bash regulären Ausdruck vs.Bash Parametererweiterung
Als Antwort von Charles Duffy funktioniert, aber nur verwendenBash regulären Ausdruck , und ich weiß, dass dies langsam ist, ich möchte einen anderen Weg zeigen, nur mitBashParametererweiterung :
Nur für positive ganze Zahlen:
Für ganze Zahlen:
Dann zum Schwimmen:
So passen Sie die ursprüngliche Anfrage an:
Nun ein kleiner Vergleich:
Es gibt den gleichen Scheck basierend auf Charles Duffys Antwort :
Einige Tests:
OK, das ist gut. Nun, wie viel Zeit wird das alles dauern (auf meinem Himbeer-Pi):
Dann mit regulären Ausdrücken:
quelle
extglob
(und etwas schneller)!Um negative Zahlen zu fangen:
quelle
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_005bSie könnten "let" auch so verwenden:
Aber ich bevorzuge es, den Operator "= ~" Bash 3+ zu verwenden, wie einige Antworten in diesem Thread.
quelle
Entfernen Sie das
-\?
In-Grep-Übereinstimmungsmuster, wenn Sie keine negative Ganzzahl akzeptieren.quelle
Habe hier dasselbe mit einem regulären Ausdruck gemacht, der den gesamten Teil und den Dezimalteil testet, getrennt durch einen Punkt.
quelle
.
a=''
und die Zeichenfolge, die nur einen Punkt enthält, abgleichen,a='.'
damit Ihr Code ein bisschen kaputt ist ...Ich benutze Folgendes (für ganze Zahlen):
quelle
-n
usw. (wegenecho
) und Sie akzeptieren Variablen mit nachgestellten Zeilenumbrüchen (wegen$(...)
). Und übrigensprint
ist kein gültiger Shell-Befehl.Ich habe das Rezept von Ultrasawblade ausprobiert, da es mir am praktischsten erschien und es nicht zum Laufen bringen konnte. Am Ende habe ich mir jedoch einen anderen Weg ausgedacht, der wie andere auf der Parametersubstitution basiert, diesmal mit Regex-Ersetzung:
Es entfernt jedes: digit: class-Zeichen in $ var und prüft, ob eine leere Zeichenfolge übrig bleibt, was bedeutet, dass das Original nur aus Zahlen besteht.
Was ich an diesem Modell mag, ist sein geringer Platzbedarf und seine Flexibilität. In dieser Form funktioniert es nur für nicht begrenzte Ganzzahlen der Basis 10, obwohl Sie sicherlich den Mustervergleich verwenden können, um ihn an andere Anforderungen anzupassen.
quelle
sed
ist POSIX, während Ihre Lösung istbash
. Beide haben ihre VerwendungQuick & Dirty: Ich weiß, dass dies nicht die eleganteste Art ist, aber ich habe normalerweise nur eine Null hinzugefügt und das Ergebnis getestet. wie so:
$ (($ 1 + 0)) gibt 0 oder bomb zurück, wenn $ 1 KEINE ganze Zahl ist. zum Beispiel:
HINWEIS: Ich denke, ich hätte nie gedacht, nach Floats oder gemischten Typen zu suchen, die die gesamte Skriptbombe erzeugen ... in meinem Fall wollte ich nicht, dass es weiter geht. Ich werde mit Mruccis Lösung und Duffys Regex herumspielen - sie scheinen die robustesten im Bash-Framework zu sein ...
quelle
1+1
, lehnt jedoch einige positive ganze Zahlen mit führenden0
s ab (da08
es sich um eine ungültige Oktalkonstante handelt).0
Ist keine Zahl und unterliegt einer willkürlichen Code-Injektion. Probieren Sie es aus :isInteger 'a[$(ls)]'
. Hoppla.$((...))
ist nicht zitiert, eine ZahlIFS=123
wird es ändern.