Ich versuche, etwas häufig genug zu tun: Benutzereingaben in einem Shell-Skript analysieren. Wenn der Benutzer eine gültige Ganzzahl angegeben hat, führt das Skript eine Sache aus, und wenn es nicht gültig ist, führt es etwas anderes aus. Das Problem ist, ich habe keinen einfachen (und einigermaßen eleganten) Weg gefunden, dies zu tun - ich möchte es nicht char für char auseinander nehmen müssen.
Ich weiß, dass das einfach sein muss, aber ich weiß nicht wie. Ich könnte es in einem Dutzend Sprachen machen, aber nicht BASH!
In meiner Forschung fand ich Folgendes:
Und darin gibt es eine Antwort, die über Regex spricht, aber soweit ich weiß, ist dies eine Funktion, die (unter anderem) in C verfügbar ist. Trotzdem hatte es eine großartige Antwort, also versuchte ich es mit grep, aber grep wusste nicht, was ich damit anfangen sollte. Ich habe -P ausprobiert, was auf meiner Box bedeutet, es als PERL-Regexp-Nada zu behandeln. Dash E (-E) hat auch nicht funktioniert. Und -F auch nicht -F.
Um ganz klar zu sein, ich versuche so etwas und suche nach einer Ausgabe - von dort aus werde ich das Skript hacken, um alles zu nutzen, was ich bekomme. (IOW, ich hatte erwartet, dass eine nicht konforme Eingabe nichts zurückgibt, während eine gültige Zeile wiederholt wird.)
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
Würde jemand bitte veranschaulichen, wie dies am einfachsten gemacht werden kann?
Ehrlich gesagt ist dies meiner Meinung nach ein Mangel an TEST. Es sollte eine Flagge wie diese haben
if [ -I "string" ] ;
then
echo "String is a valid integer."
else
echo "String is not a valid integer."
fi
quelle
[
ist alt kompatibeltest
;[[
ist Bashs neue Sache, mit mehr Operationen und anderen Angebotsregeln. Wenn Sie sich bereits entschieden haben, bei Bash zu bleiben, entscheiden Sie sich für[[
(es ist wirklich viel schöner); Wenn Sie eine Portabilität auf andere Shells benötigen, vermeiden Sie dies[[
vollständig.Antworten:
^
zeigt den Beginn des Eingabemusters an-
ist ein wörtliches "-"?
bedeutet "0 oder 1 des vorhergehenden (-
)"+
bedeutet "1 oder mehr der vorhergehenden ([0-9]
)"$
zeigt das Ende des Eingabemusters anDer reguläre Ausdruck entspricht also einer Option
-
(für den Fall negativer Zahlen), gefolgt von einer oder mehreren Dezimalstellen.Referenzen :
quelle
+
bedeutet "1 oder mehr des vorhergehenden" und das$
gibt das Ende des Eingabemusters an. Der reguläre Ausdruck entspricht also einer Option,-
gefolgt von einer oder mehreren Dezimalstellen.[A-z]
würden Sie nicht nur gebenA-Z
unda-z
aber auch\
,[
,]
,^
,_
, und`
.d[g-i]{2}
nicht nur übereinstimmen,dig
sondern auchdish
in der von dieser Antwort vorgeschlagenen Zusammenstellung (wobei dersh
Digraph als einzelnes Zeichen betrachtet wird, das danach sortiert wirdh
).Wow ... hier gibt es so viele gute Lösungen !! Von allen oben genannten Lösungen stimme ich @nortally zu, dass die Verwendung des
-eq
einen Liners am coolsten ist.Ich verwende GNU Bash, Version
4.1.5
(Debian). Ich habe dies auch auf ksh (SunSO 5.10) überprüft.Hier ist meine Version der Überprüfung, ob
$1
es sich um eine Ganzzahl handelt oder nicht:Dieser Ansatz berücksichtigt auch negative Zahlen, bei denen einige der anderen Lösungen ein fehlerhaftes negatives Ergebnis haben, und ermöglicht ein Präfix von "+" (z. B. +30), was offensichtlich eine ganze Zahl ist.
Ergebnisse:
Die von Ignacio Vazquez-Abrams bereitgestellte Lösung war auch sehr ordentlich (wenn Sie Regex mögen), nachdem sie erklärt wurde. Es werden jedoch keine positiven Zahlen mit dem
+
Präfix verarbeitet, es kann jedoch wie folgt leicht behoben werden:quelle
Nachzügler zur Party hier. Ich bin äußerst überrascht, dass in keiner der Antworten die einfachste, schnellste und tragbarste Lösung erwähnt wird. die
case
Aussage.Das Trimmen eines Zeichens vor dem Vergleich fühlt sich wie ein Hack an, aber das macht den Ausdruck für die case-Aussage so viel einfacher.
quelle
''|*[!0-9]*)
Ich mag die Lösung mit dem
-eq
Test, weil es im Grunde ein Einzeiler ist.Meine eigene Lösung bestand darin, die Parametererweiterung zu verwenden, um alle Ziffern wegzuwerfen und festzustellen, ob noch etwas übrig war. (Ich benutze immer noch 3.0, habe es noch nicht benutzt
[[
oderexpr
vorher, bin aber froh, sie kennenzulernen.)quelle
[ -z "${INPUT_STRING//[0-9]}" ]
wirklich schönen Lösung weiter verbessert werden !-eq
Lösung hat einige Probleme; siehe hier: stackoverflow.com/a/808740/1858225=~
Verwenden Sie für die Portabilität auf Pre-Bash 3.1 (als der Test eingeführt wurde)expr
.expr STRING : REGEX
Sucht nach REGEX, das zu Beginn von STRING verankert ist, wobei die erste Gruppe (oder die Länge der Übereinstimmung, falls keine vorhanden) wiedergegeben wird und Erfolg / Misserfolg zurückgegeben wird. Dies ist die alte Regex-Syntax, daher der Überschuss\
.-\?
bedeutet "vielleicht-
",[0-9]\+
bedeutet "eine oder mehrere Ziffern" und$
bedeutet "Ende der Zeichenfolge".Bash unterstützt auch erweiterte Globs, obwohl ich mich nicht mehr daran erinnere, ab welcher Version.
@(-|)
bedeutet "-
oder nichts",[0-9]
bedeutet "Ziffer" und*([0-9])
bedeutet "null oder mehr Ziffern".quelle
awk
,~
war der "Regex" Operator. In Perl (wie von C kopiert)~
wurde bereits für "Bitkomplement" verwendet, also verwendeten sie=~
. Diese spätere Notation wurde in mehrere andere Sprachen kopiert. (Perl 5.10 und Perl 6 mögen~~
mehr, aber das hat hier keine Auswirkungen.) Ich nehme an, Sie könnten es als eine Art ungefähre Gleichheit betrachten ...Hier ist noch eine andere Sichtweise (nur mit dem Befehl test builtin und seinem Rückkehrcode):
quelle
$()
mit zu verwendenif
. Das funktioniert :if is_int "$input"
. Auch das$[]
Formular ist veraltet. Verwenden Sie$(())
stattdessen. In beiden Fällen kann das Dollarzeichen weggelassen werden:echo "Integer: $((input))"
Geschweifte Klammern sind an keiner Stelle in Ihrem Skript erforderlich.test
scheint dies jedoch nicht zu unterstützen.[[
tut es aber.[[ 16#aa -eq 16#aa ]] && echo integer
druckt "Ganzzahl".[[
für diese Methode falsch positive Ergebnisse zurückgegeben werden. zB[[ f -eq f ]]
gelingt. Also muss estest
oder verwenden[
.Sie können Nicht-Ziffern entfernen und einen Vergleich durchführen. Hier ist ein Demo-Skript:
So sieht die Testausgabe aus:
quelle
${var//string}
und${var#string}
im Abschnitt "Pattern Matching" für [^ [: digit:]] `suchen (der ebenfalls behandelt wirdman 7 regex
).match=${match#0*}
nicht nicht führenden Nullen Streifen, Streifen es höchstens eine Null. Mit Expansion kann dies nur mitextglob
via erreicht werdenmatch=${match##+(0)}
.09
ist keine Ganzzahl, wenn Sie davon ausgehen, dass eine Ganzzahl keine führenden Nullen enthält. Der Test ist, ob die Eingabe (09
) einer bereinigten Version (9
- eine Ganzzahl) entspricht und nicht.Für mich war die einfachste Lösung, die Variable in einem
(())
Ausdruck wie folgt zu verwenden:Diese Lösung ist natürlich nur gültig, wenn ein Wert von Null für Ihre Anwendung keinen Sinn ergibt. Das stimmte in meinem Fall, und das ist viel einfacher als die anderen Lösungen.
Wie in den Kommentaren erwähnt, kann dies zu einem Angriff auf die Codeausführung führen: Der
(( ))
Operator wertet ausVAR
, wie imArithmetic Evaluation
Abschnitt der Manpage bash (1) angegeben . Daher sollten Sie diese Technik nicht verwenden, wenn die Quelle des Inhalts vonVAR
ungewiss ist (und natürlich auch KEINE andere Form der variablen Erweiterung verwenden).quelle
if (( var )); then echo "$var is an int."; fi
VAR='a[$(ls)]'; if ((VAR > 0)); then echo "$VAR is a positive integer"; fi
. An diesem Punkt bist du froh, dass ich stattdessen keinen bösen Befehl eingegeben habels
. Da OP Benutzereingaben erwähnt , hoffe ich wirklich, dass Sie diese nicht mit Benutzereingaben im Produktionscode verwenden!agent007
oder mit sed:
quelle
test -z "${string//[0-9]/}" && echo "integer" || echo "no integer"
... vermeiden, obwohl dies im Grunde Dennis Williamsons Antwortif [[ -n "$(printf "%s" "${2}" | sed s/[0-9]//g)" ]]; then
Hinzufügen zur Antwort von Ignacio Vazquez-Abrams. Dadurch kann das + -Zeichen vor der Ganzzahl stehen, und es können beliebig viele Nullen als Dezimalstellen verwendet werden. Auf diese Weise kann beispielsweise +45.00000000 als Ganzzahl betrachtet werden.
$ 1 muss jedoch so formatiert sein, dass es einen Dezimalpunkt enthält. 45 wird hier nicht als Ganzzahl betrachtet, 45.0 jedoch.
quelle
^[-+]?[0-9]
... verwenden?Zum Lachen habe ich ungefähr schnell eine Reihe von Funktionen ausgearbeitet, um dies zu tun (is_string, is_int, is_float, is alpha string oder andere), aber es gibt effizientere (weniger Code) Möglichkeiten, dies zu tun:
Führen Sie hier einige Tests durch, ich habe definiert, dass -44 ein int ist, aber 44- nicht etc ..:
Ausgabe:
HINWEIS: Führende Nullen können beim Hinzufügen von Zahlen wie Oktal auf etwas anderes schließen. Daher ist es besser, sie zu entfernen, wenn Sie beabsichtigen, '09' als int zu behandeln (was ich tue) (z. B.
expr 09 + 0
oder mit sed entfernen).quelle