Wie teste ich, ob eine Variable eine Zahl in Bash ist?


599

Ich kann einfach nicht herausfinden, wie ich sicherstellen kann, dass ein an mein Skript übergebenes Argument eine Zahl ist oder nicht.

Ich möchte nur so etwas tun:

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

Irgendeine Hilfe?


17
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 1
fi

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:

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

7
+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 ( ?*-*/ *.*.*)


20
+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.
Tripleee

Siehe dies für die Behandlung von signierten Ganzzahlen: stackoverflow.com/a/18620446/2013911
Niklas Peter

2
@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")

8
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";

5
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".
Studgeek

Beide scheitern mit122s :-(
Hastur

40

Niemand schlug Bashs erweiterten Mustervergleich vor :

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"

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

Ändern Sie '% f' in das gewünschte Format.


4
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.

So testen Sie direkt in der Befehlszeile:

$ echo "32" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is: 32

$ echo "3a2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is empty (false)

$ echo ".5" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer .5

$ echo "3.2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is 3.2

Verwenden in einem Bash-Skript:

check=`echo "$1" | grep -E ^\-?[0-9]*\.?[0-9]+$`

if [ "$check" != '' ]; then    
  # it IS numeric
  echo "Yeap!"
else
  # it is NOT numeric.
  echo "nooop"
fi

Verwenden Sie Folgendes, um NUR Ganzzahlen abzugleichen:

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`

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

4
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.
user2683246

@sjas ok, meine Schuld
user2683246

@sjasecho $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
user2683246

9

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")

Dies ist eher wie isInteger. Aber super danke!
Naheel

8

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.


Dies schlägt für eine leere fehl var.
gniourf_gniourf

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}"
    done

    for value in ${float_values}
    do
        assertTrue "${value} should be tested as float" "isFloat ${value}"
        assertFalse "${value} should not be tested as integer" "isInteger ${value}"
    done

}

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.


Es tut mir leid, ich habe nicht verstanden, wie die Bearbeitungsfunktion verwendet werden soll.
3ronco

6

Ich würde das versuchen:

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

Hinweis: Dies erkennt nan und inf als Zahl.


2
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:

parse_num() {
 local r=`expr "$1" : '.*\([.,]\)' 2>/dev/null | tr -d '\n'` 
 nat='^[+-]?[0-9]+[.,]?$' \
 dot="${1%[.,]*}${r}${1##*[.,]}" \
 float='^[\+\-]?([.,0-9]+[Ee]?[-+]?|)[0-9]+$'
 [[ "$1" == $dot ]] && [[ "$1" =~ $float ]] || [[ "$1" =~ $nat ]]
} # usage: parse_num -123.456

6
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

Vergessen Sie nicht, -negative Zahlen anzugeben!


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=1120
if 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=1120a
if 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.01
if 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.01
if 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).
Charles Duffy

4

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

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

17
[[ $VAR = *[[:digit:]]* ]]gibt true zurück, wenn die Variable eine Zahl enthält , nicht wenn es sich um eine Ganzzahl handelt.
Glenn Jackman

[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"druckt numeric. In Bash-Version getestet 3.2.25(1)-release.
Jdamian

1
@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

2
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:

#!/bin/bash

    # N={0,1,2,3,...} by syntaxerror
function isNaturalNumber()
{
 [[ ${1} =~ ^[0-9]+$ ]]
}
    # Z={...,-2,-1,0,1,2,...} by karttu
function isInteger() 
{
 [[ ${1} == ?(-)+([0-9]) ]]
}
    # Q={...,-½,-¼,0.0,¼,½,...} by karttu
function isFloat() 
{
 [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
    # R={...,-1,-½,-¼,0.E+n,¼,½,1,...}
function isNumber()
{
 isNaturalNumber $1 || isInteger $1 || isFloat $1
}

bools=("TRUE" "FALSE")
int_values="0 123 -0 -123"
float_values="0.0 0. .0 -0.0 -0. -.0 \
    123.456 123. .456 -123.456 -123. -.456 \
    123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
    123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
    123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
false_values="blah meh mooh blah5 67mooh a123bc"

for value in ${int_values} ${float_values} ${false_values}
do
    printf "  %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value)
    printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value)
    printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value)
    printf "%5s=%-24s\n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value)
done

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:

isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]]; }
isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; }
isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; }

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>&1
then
    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.


1
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?

if [ "$var" -ge 0 ] 2> /dev/null; then ..

2

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=1 
  local -i FAIL=0   
  case "${NUMBER_TYPE}" in 
    "${CHECK_FLOAT}") 
        if ((! $(printf "${CHECK_FLOAT}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
    "${CHECK_INTEGER}") 
        if ((! $(printf "${CHECK_INTEGER}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
                     *) 
        echo "Invalid number type format: ${NUMBER_TYPE} to check_number()." 1>&2
        echo "${FAIL}"
        ;;                 
   esac
} 

>$ var=45

>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }


2

regulären Ausdruck vs. Parametererweiterung

Als Antwort von Charles Duffy funktioniert, aber nur verwenden regulären Ausdruck , und ich weiß, dass dies langsam ist, ich möchte einen anderen Weg zeigen, nur mitParametererweiterung :

Nur für positive ganze Zahlen:

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

Für ganze Zahlen:

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

Dann zum Schwimmen:

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

So passen Sie die ursprüngliche Anfrage an:

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

Nun ein kleiner Vergleich:

Es gibt den gleichen Scheck basierend auf Charles Duffys Antwort :

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

Einige Tests:

if is_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if is_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number

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.


5
Das ist sehr gefährlich. Bewerten Sie keine nicht validierte Arithmetik in der Shell. Es muss zuerst auf eine andere Weise validiert werden.
Ormaaj

1
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

Entfernen Sie das -\?In-Grep-Übereinstimmungsmuster, wenn Sie keine negative Ganzzahl akzeptieren.


2
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.

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

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi

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 
      return 1
  fi
    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 ...


4
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.
Isaac
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.