it-swarm.com.de

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

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

Alles was ich tun möchte ist so etwas:

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

Irgendeine Hilfe?

482
Flávio Amieiro

Ein Ansatz besteht darin, einen regulären Ausdruck zu verwenden:

re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
   echo "error: Not a number" >&2; exit 1
fi

Wenn der Wert nicht unbedingt eine ganze Zahl ist, sollten Sie die Regex entsprechend ändern. zum Beispiel:

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

... oder um Zahlen mit einem Zeichen zu behandeln:

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

Ohne Basismen (funktioniert auch im System V sh),

case $string in
    ''|*[!0-9]*) echo bad ;;
    *) echo good ;;
esac

Dies lehnt leere Zeichenfolgen und Zeichenfolgen ab, die Nicht-Ziffern enthalten, und akzeptiert alles andere.

Negative oder Fließkommazahlen erfordern zusätzliche Arbeit. Eine Idee ist, -/. im ersten "schlechten" Muster auszuschließen und weitere "schlechte" Muster hinzuzufügen, die die unangemessene Verwendung enthalten (?*-*/*.*.*)

232
jilles

Die folgende Lösung kann auch in einfachen Shells wie Bourne verwendet werden, ohne dass reguläre Ausdrücke erforderlich sind. Grundsätzlich führen alle numerischen Wertevaluierungsoperationen, die Nichtzahlen verwenden, zu einem Fehler, der implizit in Shell 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 auf $ 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 "Integer-Ausdruck erwartet" auszublenden, die von bash ausgegeben wird, falls keine Zahl vorhanden ist.

VORBEHALTE(dank der Kommentare unten):

  • Zahlen mit Dezimalpunkt sind nicht als gültige "Zahlen" gekennzeichnet
  • Die Verwendung von [[ ]] anstelle von [ ] wird immer zu true ausgewertet.
  • 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 nach der Zahl Leerzeichen enthält (z. B. "1 a"), wird ein Fehler ausgegeben, z. B. 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 ausgegeben, z. B. bash: [[: i: expression recursion level exceeded (error token is "i").
154

Dies prüft, ob eine Zahl eine nicht negative ganze Zahl ist und sowohl von der Shell unabhängig ist (d. H. Ohne Bashismen), als nur die integrierten Shell-Ins:

[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";

ABER IS FALSCH.
Wie jilles kommentiert und in seine Antwort vorgeschlagen ist dies der richtige Weg, dies mit Shell-Mustern zu tun.

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";
33
mrucci

Niemand hat vorgeschlagen bash erweiterte Musterübereinstimmung :

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

Ich bin überrascht, dass die Lösungen Zahlenformate direkt in Shell analysieren Shell eignet sich nicht dafür, da es sich um eine DSL-Steuerung für Dateien und Prozesse handelt

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.

27
pixelbeat

Ich sah mir die Antworten an und stellte fest, dass niemand an FLOAT-Nummern (mit Punkt) dachte!

Die Verwendung von grep ist auch großartig.
- E bedeutet erweiterter Ausdruck
- q bedeutet leise (kein Echo)
- qE ist die Kombination aus 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

Um JUST-Ganzzahlen zu finden, verwenden Sie Folgendes:

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

Nur ein Anschluss an @mary. Aber da ich nicht genügend Vertreter 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 Floats. 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
12
triple_r

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 Fließkommazahl.

Wenn Sie jedoch eine Hexadezimalzahl im C-Stil angeben, d. H. "0xffff" oder "0XFFFF", gibt [[: digit:]] "true" zurück. Ein bisschen Trap hier. Mit bash können Sie so etwas wie "0xAZ00" tun und es immer noch als eine Ziffer zählen (ist dies nicht ein Teil der komischen GCC-Compiler, bei dem Sie die 0x-Notation für andere Basen als 16 verwenden können ??? )

Möglicherweise möchten Sie vor dem Test auf "0x" oder "0X" testen, ob es sich um eine numerische Zahl handelt, wenn Ihre Eingabe vollständig nicht vertrauenswürdig ist, es sei denn, Sie möchten Hexadezimalzahlen akzeptieren. Das würde erreicht werden durch:

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi
7
ultrasawblade

Alte Frage, aber ich wollte nur meine Lösung angehen. Dieses erfordert keine seltsamen Shell-Tricks oder verlässt sich auf etwas, das es noch nie gegeben hat.

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 mit einer Länge ungleich Null haben, ist diese keine Zahl.

7
Sammitch
test -z "${i//[0-9]}" && echo digits || echo no no no

${i//[0-9]} ersetzt jede Ziffer im Wert von $i durch eine leere Zeichenfolge, siehe man -P 'less +/parameter\/' bash. -z prüft, ob die resultierende Zeichenfolge null ist.

wenn Sie auch den Fall ausschließen möchten, wenn $i leer 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
6
user2683246
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

Vergessen Sie nicht, dass - negative Zahlen enthält!

6
D_I

Ich kann noch nicht kommentieren, also füge ich meine eigene Antwort hinzu, die eine Erweiterung der Antwort von glenn jackman mit dem bash-Pattern-Matching ist.

Mein ursprüngliches Bedürfnis bestand darin, Zahlen zu identifizieren und ganze Zahlen und Floats zu unterscheiden. Die Funktionsdefinitionen abgezogen an:

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 bestätigen, dass meine Muster wie erwartet 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 geändert werden, um den Dezimalpunkt (@(.,)) und das E-Symbol (@(Ee)) toleranter zu machen. Mein Unit-Test testet nur Werte, die entweder eine Ganzzahl oder ein Gleitkomma sind, jedoch keine ungültigen Eingaben.

5
karttu

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.

5
overflowed

Am einfachsten prüfen Sie, ob es nicht-numerische Zeichen enthält. Sie ersetzen alle Ziffern durch nichts und prüfen auf Länge. Wenn es Länge gibt, ist es keine Zahl.

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

Eine eindeutige 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

Bei reellen Zahlen ist es jedoch nicht zwingend, vor dem radix-Punkt eine Zahl zu haben.

Um eine gründlichere Unterstützung für Floating-Nummern und wissenschaftliche Notationen bereitzustellen (viele Programme in C/Fortran oder werden auf diese Weise Float exportiert), wäre eine sinnvolle Ergänzung 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

Wenn Sie nach einem bestimmten Typ suchen, führt dies zu einer Möglichkeit, die Nummernarten zu unterscheiden:

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önnen die syntaktischen Anforderungen für die Dezimal- und wissenschaftliche Notation auflisten, wobei Kommas als Radix-Punkt sowie "." Zulässig ist. Wir würden dann behaupten, dass es nur einen solchen Radixpunkt geben muss. [Ee] Float kann zwei Vorzeichen enthalten. Ich habe ein paar weitere Regeln aus Aulu's Arbeit gelernt und mit schlechten Zeichenketten wie '' '-' '-E-1' '0-0' getestet. Hier sind meine Tools für Regex/Teilstring/Ausdruck, die sich zu halten 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
4
Aulo

Ich benutze expr . Wenn Sie versuchen, einem nicht-numerischen Wert eine Null hinzuzufügen, wird eine Nicht-Null zurückgegeben:

if expr -- "$number" + 0 > /dev/null 2>&1
then
    echo "$number is a number"
else
    echo "$number isn't a number"
fi

Es kann möglich sein, bc zu verwenden, wenn Sie nicht ganze Zahlen benötigen, aber ich glaube nicht, dass bc das gleiche Verhalten hat. Wenn Sie einer Nicht-Zahl Null hinzufügen, erhalten Sie Null und es wird auch ein Wert von Null zurückgegeben. Vielleicht können Sie bc und expr kombinieren. Verwenden Sie bc, um Null zu $number hinzuzufügen. Wenn die Antwort 0 lautet, überprüfen Sie mit expr, ob $number nicht Null ist.

4
David W.

Da musste ich mich in letzter Zeit etwas ändern und wie karttu's / Appoach 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 enthält isNumber () Bindestriche, Kommas und Exponentialschreibweise und gibt daher TRUE für ganze Zahlen und Floats zurück, wohingegen isFloat () für Integer-Werte FALSE und isInteger () zurückgibt FALSCH auf Schwimmern. 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; }
3
3ronco

Dies kann erreicht werden, indem Sie mit grep prüfen, ob die betreffende Variable mit einem erweiterten regulären Ausdruck übereinstimmt.

Test Ganzzahl 1120:

yournumber=1120
if [ $(echo "$yournumber" | grep -qE '^[0-9]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Ausgabe: Valid number.

Test nicht ganzzahliger 1120a:

yournumber=1120a
if [ $(echo "$yournumber" | grep -qE '^[0-9]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Ausgabe: Error: not a number.


Erläuterung

  • Die grep, der -E-Schalter ermöglicht die Verwendung des erweiterten regulären Ausdrucks '^[0-9]+$'. Dieser reguläre Ausdruck bedeutet, dass die Variable nur [] die Zahlen 0-9 von null bis neun vom ^ bis zum $-Ende der Variablen enthalten sollte und mindestens + ein Zeichen enthalten sollte.
  • Die grep, der -q quiet-Schalter schaltet jeden Ausgang aus, unabhängig davon, ob er etwas findet oder nicht.
  • $? ist der Beendigungsstatus des zuvor ausgeführten Befehls. Exit-Status 0 bedeutet Erfolg und alles Größere bedeutet Fehler. Der Befehl grep hat den Exit-Status 0, wenn er eine Übereinstimmung findet, und 1, wenn dies nicht der Fall ist.
  • $() ist eine Subshell, mit der wir einen anderen Befehl ausführen und dann die Ausgabe verwenden können.

Zusammengefasst, in einer $()-Subshell, echo die Variable $yournumber und | leiten es an grep, die mit dem -q-Schalter still den -E-erweiterten regulären Ausdruck '^[0-9]+$' passt. Wir echo den $?-Exit-Status. Dies wäre 0, wenn grep erfolgreich eine Übereinstimmung gefunden hat, und 1, falls dies nicht der Fall ist.

Jetzt außerhalb der $()-Subshell und zurück in der if-Bedingung, nehmen wir die Ausgabe entweder 0 oder 1 aus der $()-Subshell und prüfen, ob -ne ungleich "0" ist. Wenn dies nicht der Fall ist, lautet der Beendigungsstatus 1, der nicht mit "0" übereinstimmt. Dann werden wir echo "Error: not a number.". Wenn die Übereinstimmung erfolgreich war, würde die Ausgabe des Exit-Status 0 lauten, was gleich "0" ist, und andernfalls wir echo "Valid number.".


Für Floats oder Doubles

Wir können den regulären Ausdruck für Floats oder Doubles einfach von '^[0-9]+$' in '^[0-9]*+\.?[0-8]+$' ändern.

Test Float 1120.01:

yournumber=1120.01
if [ $(echo "$yournumber" | grep -qE '^[0-9]*+\.?[0-8]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Ausgabe: Valid number.

Test Float 11.20.01:

yournumber=11.20.01
if [ $(echo "$yournumber" | grep -qE '^[0-9]*+\.?[0-8]+$'; echo $?) -ne "0" ]; then
    echo "Error: not a number."
else
    echo "Valid number."
fi

Ausgabe: Error: not a number.


Für Negative

Um negative Ganzzahlen zuzulassen, ändern Sie einfach den regulären Ausdruck von '^[0-9]+$' in '^\-?[0-9]+$'.

Um negative Fließ- oder Doppelpunkte zuzulassen, ändern Sie einfach den regulären Ausdruck von '^[0-9]*+\.?[0-8]+$' in '^\-?[0-9]*+\.?[0-8]+$'.

2
Joseph Shih

Das gleiche hier mit einem regulären Ausdruck, 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
1
Jerome

Ich habe das Rezept von Ultrasawblade ausprobiert, da es mir am praktischsten erschien und es nicht funktionieren konnte. Am Ende habe ich jedoch einen anderen Weg gefunden, basierend auf anderen Parametern, diesmal mit Regex-Ersetzung:

[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"

Es entfernt alle: digit: class-Zeichen in $ var und prüft, ob noch eine leere Zeichenfolge vorhanden ist. Dies bedeutet, dass das Original nur Zahlen war.

Was mir an diesem Gerät gefällt, ist seine geringe Stellfläche und Flexibilität. In dieser Form funktioniert es nur für nicht begrenzte 10-Ganzzahlen, obwohl Sie Pattern Matching verwenden können, um es an andere Bedürfnisse anzupassen.

1
ata

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
[ ~]$ 

Ich bevorzuge jedoch den "= ~" Bash 3+ -Operator, wie einige Antworten in diesem Thread.

1
Idriss Neumann
  • zu überprüfende Variable

    number=12345 oder number=-23234 oder number=23.167 oder number=-345.234

  • Überprüfen Sie numerisch oder nicht numerisch

    echo $number | grep -E '^-?[0-9]*\.?[0-9]*$' > /dev/null

  • entscheiden Sie sich für weitere Aktionen auf der Grundlage des Beendigungsstatus der oben genannten

    if [ $? -eq 0 ]; then echo "Numeric"; else echo "Non-Numeric"; fi

1
Atanu

Wenn Sie David Ws Antwort aus dem Oktober '13 verfolgen, könnte dies bei Verwendung von expr besser sein

test_var=`expr $am_i_numeric \* 0` >/dev/null 2>&1
if [ "$test_var" = "" ]
then
    ......

Bei Zahlen, multipliziert mit 1, erhalten Sie denselben Wert (einschließlich negativer Zahlen). Ansonsten erhalten Sie null, die Sie testen können 

1
Jon T

Ich benutze printf als andere Antwort, wenn Sie die Formatzeichenfolge "% f" oder "% i" angeben, wird printf die Überprüfung für Sie durchführen. Die Syntax ist einfacher als die Neuerfindung der Prüfungen, sie ist einfach und kurz und printf ist allgegenwärtig. Daher ist es meiner Meinung nach eine gute Wahl - Sie können auch die folgende Idee verwenden, um nach einer Reihe von Dingen zu suchen. Sie 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; }

1
user4401178

Um negative Zahlen zu fangen:

if [[ $1 == ?(-)+([0-9.]) ]]
    then
    echo number
else
    echo not a number
fi
1
user28490

Ich habe eine recht kurze Version gefunden:

function isnum()
{
    return `echo "$1" | awk -F"\n" '{print ($0 != $0+0)}'`
}
1
mary
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

Entfernen Sie den -\? im grep-Abgleichmuster, wenn Sie keine negative Ganzzahl akzeptieren.

1
Ane Dijitak

Für mein Problem musste ich nur sicherstellen, dass ein Benutzer nicht versehentlich Text eingibt, und ich habe versucht, ihn einfach und lesbar zu halten

isNumber() {
    (( $1 )) 2>/dev/null
}

Laut Manpage macht das so ziemlich das, was ich will

Wenn der Wert des Ausdrucks nicht Null ist, ist der Rückgabestatus 0

Um unangenehme 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")
1
Hachi

Ich mag die Antwort von Alberto Zaccagni.

if [ "$var" -eq "$var" ] 2>/dev/null; then

Wichtige Voraussetzungen: - Es wurden keine Subshells erstellt - Keine RE-Parser wurden aufgerufen - Die meisten Shell-Anwendungen verwenden keine reellen Zahlen

Wenn $var jedoch komplex ist (z. B. ein assoziativer Array-Zugriff) und die Zahl eine nicht negative ganze Zahl ist (die meisten Anwendungsfälle), ist dies möglicherweise effizienter.

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

Quick & Dirty: Ich weiß, dass dies nicht der eleganteste Weg 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 Bombe zurück, wenn $ 1 KEINE Ganzzahl 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 daran gedacht, nach Floats oder gemischten Typen zu suchen, mit denen die gesamte Skriptbombe bombiert wird. Ich werde mit mruccis Lösung und Duffys Regex herumspielen - sie scheinen die robustesten im Bash-Rahmen zu sein ... 

1
WWWIZARDS

Ich verwende 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
1
Marnix

Wenn Sie Perl installiert haben, ist dieser eine Liner einfach, lesbar und erweiterbar

Perl -se 'exit($n !~ /\d+/)' -- -n=a

Hier sind einige Tests

Perl -se 'exit($n !~ /\d+/)' -- -n=a; echo $?
1
Perl -se 'exit($n !~ /\d+/)' -- -n=2; echo $?
0

Es ist ziemlich selbst erklärt, aber hier sind mehr Informationen

  • -e ermöglicht die Auswertung
  • -s ermöglicht das Übergeben von Argumenten nach - in diesem Fall -n
  • !~ ist der Negationsregexp-Übereinstimmungsoperator. Da 0 in bash Erfolg ist, möchten wir, dass er erfolgreich beendet wird, wenn das Argument -n eine Zahl ist

Sie möchten dies in eine Funktion einschließen, hier eine bessere Version, die auch Floats lesen kann

is_number() { Perl -se 'exit($n !~ /^\d+(\.\d+)?$/)' -- -n="$1"; }
0
geckos