894 Stimmen

Wie kann ich in der Bash testen, ob eine Variable eine Zahl ist?

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

Alles, was ich tun möchte, ist so etwas wie das hier:

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

Gibt es Hilfe?

21 Stimmen

Nebenbei bemerkt - die test && echo "foo" && exit 0 || echo "bar" && exit 1 Ansatz, den Sie verwenden, kann einige unbeabsichtigte Nebeneffekte haben -- wenn das Echo fehlschlägt (vielleicht ist die Ausgabe an einen geschlossenen FD), wird die exit 0 wird übersprungen, und der Code wird dann versuchen echo "bar" . Scheitert sie auch daran, wird die && wird fehlschlagen, und es wird nicht einmal ausgeführt exit 1 ! Mit aktuellen if Erklärungen und nicht && / || ist weniger anfällig für unerwartete Nebenwirkungen.

0 Stimmen

@CharlesDuffy Das ist die Art von wirklich cleverem Denken, zu dem die meisten Leute nur kommen, wenn sie haarige Bugs aufspüren müssen...! Ich hätte nie gedacht, dass Echo einen Fehler zurückgeben könnte.

13 Stimmen

Ich bin zwar etwas spät dran, aber ich weiß um die Gefahren, über die Charles geschrieben hat, da ich sie vor einiger Zeit auch durchmachen musste. Hier ist also 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 die Dinge NICHT in einer Subshell ausgeführt werden sollen (was bei () stattdessen Klammern verwenden). Vorbehalt: Niemals das letzte Semikolon vergessen . Andernfalls könnten Sie bash um die hässlichsten (und sinnlosesten) Fehlermeldungen auszugeben...

7voto

Aulo Punkte 131

Eine klare Antwort wurde bereits von @charles Dufy und anderen gegeben. Eine reine Bash-Lösung wäre die Verwendung der folgenden :

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 .

Um eine gründlichere Unterstützung von Fließkommazahlen und wissenschaftlicher Notation zu bieten (viele Programme in C/Fortran oder anderen Sprachen 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 Unterscheidung der Zahlentypen, 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

Anmerkung: Wir könnten die syntaktischen Anforderungen für die dezimale und die wissenschaftliche Notation auflisten, wobei eine davon darin besteht, das Komma als Radixpunkt zuzulassen, ebenso wie das ".". Wir würden dann behaupten, dass es nur einen solchen Radixpunkt geben darf. In einer [Ee]-Fließkommazahl können zwei Vorzeichen vorkommen. Ich habe noch ein paar weitere Regeln aus Aulus Arbeit gelernt und gegen schlechte Zeichenketten wie '' '-' '-E-1' '0-0' getestet. Hier sind meine Regex/Substring/Expr-Tools, die sich zu bewähren scheinen:

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

7voto

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

Vergessen Sie nicht - um negative Zahlen einzubeziehen!

6voto

David W. Punkte 101611

Yo uso expr . Es gibt einen Wert ungleich Null zurück, wenn Sie versuchen, eine Null zu einem nicht-numerischen Wert zu addieren:

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, Folgendes zu verwenden bc wenn Sie Nicht-Ganzzahlen benötigen, aber ich glaube nicht, dass bc hat das gleiche Verhalten. Wenn man Null zu einer Nicht-Zahl addiert, erhält man Null, und der Rückgabewert ist ebenfalls Null. Vielleicht können Sie kombinieren bc y expr . Verwenden Sie bc um Null zu addieren $number . Wenn die Antwort lautet 0 , dann versuchen Sie expr um zu überprüfen, dass $number nicht Null ist.

6voto

Andrew Punkte 3305

Eine einfache Möglichkeit besteht darin, zu prüfen, ob er nicht-ziffrige Zeichen enthält. Man ersetzt alle Ziffern durch nichts und prüft auf Länge. Wenn es eine Länge gibt, ist es keine Zahl.

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi

5voto

3ronco Punkte 516

Da ich in letzter Zeit damit herumhantieren musste und wie karttu's mit dem Unit-Test am meisten zu tun. 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

Alors isNumber() schließt Bindestriche, Kommas und die Exponentialschreibweise ein und liefert daher TRUE für Ganzzahlen und Gleitkommazahlen, während andererseits isFloat() gibt bei ganzzahligen Werten FALSE zurück und isInteger() gibt bei Floats ebenfalls FALSE zurück. Für Ihre Bequemlichkeit alle als Einzeiler:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X