it-swarm.com.de

Testen Sie, ob String eine gültige Ganzzahl ist

Ich versuche, etwas Gemeinsames zu tun: Analysiere Benutzereingaben in einem Shell-Skript. Wenn der Benutzer eine gültige Ganzzahl angegeben hat, führt das Skript eine Sache aus, und wenn es nicht gültig ist, wird etwas anderes ausgeführt. Das Problem ist, ich habe keinen einfachen (und einigermaßen eleganten) Weg gefunden, dies zu tun - ich möchte nicht, dass es Char für Char getrennt wird.

Ich weiß, dass das einfach sein muss, aber ich weiß nicht wie. Ich könnte es in einem Dutzend Sprachen tun, aber nicht BASH!

Bei meiner Recherche habe ich folgendes gefunden:

Regulärer Ausdruck zum Testen, ob eine Zeichenfolge aus einer gültigen reellen Zahl in der Basis 10 besteht

Und da gibt es eine Antwort, die über Regex spricht, aber soweit ich weiß, ist dies eine Funktion, die in C (unter anderem) 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 versucht, was auf meiner Box bedeutet, es als Perl-Regex-Nada zu behandeln. Dash E (-E) funktionierte auch nicht. Und auch nicht -F.

Um es klar zu sagen, ich versuche so etwas und suche nach jeder Ausgabe - von dort hacke ich das Skript, 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

Kann jemand bitte erläutern, wie das am einfachsten geht?

Ehrlich gesagt ist dies meiner Meinung nach ein Mangel an TEST. Es sollte so eine Flagge haben

if [ -I "string" ] ;
then
   echo "String is a valid integer."
else
   echo "String is not a valid integer."
fi
98
Richard T
[[ $var =~ ^-?[0-9]+$ ]]
  • Der ^ gibt den Beginn des Eingabemusters an
  • Der - ist ein wörtliches "-"
  • Der ? bedeutet "0 oder 1 des vorhergehenden (-)"
  • Der + bedeutet "1 oder mehr der vorhergehenden ([0-9])"
  • Der $ zeigt das Ende des Eingabemusters an

Der Regex entspricht also einem optionalen - (für negative Zahlen), gefolgt von einer oder mehreren Dezimalstellen.

Verweise:

156

Wow ... es gibt hier so viele gute Lösungen !! Von allen oben genannten Lösungen stimme ich @nortally zu, dass die Verwendung des -eq-Liners der coolste 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 eine ganze Zahl ist oder nicht:

if [ "$1" -eq "$1" ] 2>/dev/null
then
    echo "$1 is an integer !!"
else
    echo "ERROR: first parameter must be an integer."
    echo $USAGE
    exit 1
fi

Dieser Ansatz berücksichtigt auch negative Zahlen, wobei einige der anderen Lösungen ein fehlerhaftes negatives Ergebnis haben werden, und es wird ein Präfix von "+" (z. B. +30) zugelassen, der offensichtlich eine ganze Zahl ist.

Ergebnisse:

$ int_check.sh 123
123 is an integer !!

$ int_check.sh 123+
ERROR: first parameter must be an integer.

$ int_check.sh -123
-123 is an integer !!

$ int_check.sh +30
+30 is an integer !!

$ int_check.sh -123c
ERROR: first parameter must be an integer.

$ int_check.sh 123c
ERROR: first parameter must be an integer.

$ int_check.sh c123
ERROR: first parameter must be an integer.

Die von Ignacio Vazquez-Abrams angebotene Lösung war auch sehr ordentlich (wenn Sie Regex mögen), nachdem sie erklärt wurde. Es verarbeitet jedoch keine positiven Zahlen mit dem Präfix +, es kann jedoch wie folgt korrigiert werden:

[[ $var =~ ^[-+]?[0-9]+$ ]]
58
Peter Ho

Latecomer 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-Anweisung.

case ${variable#[-+]} in
  *[!0-9]* | '') echo Not a number ;;
  * ) echo Valid number ;;
esac

Das Beschneiden eines beliebigen Zeichens vor dem Vergleich fühlt sich an wie ein Hack, aber das macht den Ausdruck für die case-Anweisung so viel einfacher.

24
tripleee

Für die Portabilität zu Pre-Bash 3.1 (als der =~-Test eingeführt wurde) verwenden Sie expr.

if expr "$string" : '-\?[0-9]\+$' >/dev/null
then
  echo "String is a valid integer."
else
  echo "String is not a valid integer."
fi

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 ist) und der Erfolg/Fehler zurückgegeben wird. Dies ist alte Regex-Syntax, daher der überschüssige \. -\? bedeutet "vielleicht -", [0-9]\+ bedeutet "eine oder mehrere Ziffern" und $ bedeutet "Ende der Zeichenfolge".

Bash unterstützt auch erweiterte Globs, obwohl ich mich nicht an welche Version erinnere.

shopt -s extglob
case "$string" of
    @(-|)[0-9]*([0-9]))
        echo "String is a valid integer." ;;
    *)
        echo "String is not a valid integer." ;;
esac

# equivalently, [[ $string = @(-|)[0-9]*([0-9])) ]]

@(-|) bedeutet "- oder nichts", [0-9] bedeutet "Ziffer" und *([0-9]) bedeutet "null oder mehr Ziffern".

9
ephemient

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 Zahlen wegzuwerfen und zu sehen, ob noch etwas übrig war. (Ich verwende immer noch 3.0, habe [[ oder expr noch nicht verwendet, bin aber froh, sie kennenzulernen.)

if [ "${INPUT_STRING//[0-9]}" = "" ]; then
  # yes, natural number
else
  # no, has non-numeral chars
fi
8
nortally

Hier ist noch eine andere Version (nur mit dem Befehl test builtin und dem Rückkehrcode):

function is_int() { return $(test "[email protected]" -eq "[email protected]" > /dev/null 2>&1); } 

input="-123"

if $(is_int "${input}");
then
   echo "Input: ${input}"
   echo "Integer: $[${input}]"
else
   echo "Not an integer: ${input}"
fi
3
hans

Für mich war die einfachste Lösung, die Variable innerhalb eines (())-Ausdrucks zu verwenden:

if ((VAR > 0))
then
  echo "$VAR is a positive integer."
fi

Diese Lösung ist natürlich nur gültig, wenn der Wert Null für Ihre Anwendung nicht sinnvoll ist. Das war in meinem Fall so, und das ist viel einfacher als bei den anderen Lösungen.

Wie in den Kommentaren darauf hingewiesen, kann dies zu einem Code-Ausführungsangriff führen: Der (( ))-Operator wertet VAR aus, wie im Abschnitt Arithmetic Evaluation von the bash (1) man page angegeben. Daher sollten Sie diese Technik nicht verwenden, wenn die Quelle des Inhalts von VAR unsicher ist (oder Sie sollten natürlich JEDE andere Form der Variablenerweiterung verwenden).

2
Trebor Rude

Sie können Nicht-Ziffern entfernen und einen Vergleich durchführen. Hier ist ein Demo-Skript:

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09"
do
    match=${num//[^[:digit:]]}    # strip non-digits
    match=${match#0*}             # strip leading zeros
    echo -en "$num\t$match\t"
    case $num in
        $match|-$match)    echo "Integer";;
                     *)    echo "Not integer";;
    esac
done

So sieht die Testausgabe aus:

 44 44 Ganzzahl 
- 44 44 Ganzzahl 
 44- 44 Nicht-Ganzzahl 
 4-4 44 Nicht-Ganzzahl 
 A4 4 Nicht-Ganzzahl 
 4a 4 Nicht-Ganzzahl 
. 4 4 Nicht ganze Zahl __. 4.4 44 Nicht ganze Zahl __.- 4.4 44 Nicht ganze Zahl __. 09 9 Nicht ganze Zahl __.
2

Zum Lachen habe ich grob 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: 

#!/bin/bash

function strindex() {
    x="${1%%$2*}"
    if [[ "$x" = "$1" ]] ;then
        true
    else
        if [ "${#x}" -gt 0 ] ;then
            false
        else
            true
        fi
    fi
}

function is_int() {
    if is_empty "${1}" ;then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^0-9]*//g')
    if [[ $tmp == "${1}" ]] || [[ "-${tmp}" == "${1}" ]] ; then
        #echo "INT (${1}) tmp=$tmp"
        true
    else
        #echo "NOT INT (${1}) tmp=$tmp"
        false
    fi
}

function is_float() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if ! strindex "${1}" "-" ; then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^a-z. ]*//g')
    if [[ $tmp =~ "." ]] ; then
        #echo "FLOAT  (${1}) tmp=$tmp"
        true
    else
        #echo "NOT FLOAT  (${1}) tmp=$tmp"
        false
    fi
}

function is_strict_string() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if [[ "${1}" =~ ^[A-Za-z]+$ ]]; then
        #echo "STRICT STRING (${1})"
        true
    else
        #echo "NOT STRICT STRING (${1})"
        false
    fi
}

function is_string() {
    if is_empty "${1}" || is_int "${1}" || is_float "${1}" || is_strict_string "${1}" ;then
        false
        return
    fi
    if [ ! -z "${1}" ] ;then
        true
        return
    fi
    false
}
function is_empty() {
    if [ -z "${1// }" ] ;then
        true
    else
        false
    fi
}

Ich habe hier einige Tests durchgelaufen. Ich habe definiert, dass -44 ein int ist, aber 44- ist nicht etc ..:

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09" "hello" "h3llo!" "!!" " " "" ; do
    if is_int "$num" ;then
        echo "INT = $num"

    Elif is_float "$num" ;then
        echo "FLOAT = $num"

    Elif is_string "$num" ; then
        echo "STRING = $num"

    Elif is_strict_string "$num" ; then
        echo "STRICT STRING = $num"
    else
        echo "OTHER = $num"
    fi
done

Ausgabe:

INT = 44
INT = -44
STRING = 44-
STRING = 4-4
STRING = a4
STRING = 4a
FLOAT = .4
FLOAT = 4.4
FLOAT = -4.4
INT = 09
STRICT STRING = hello
STRING = h3llo!
STRING = !!
OTHER =  
OTHER = 

HINWEIS: Führende Nullen könnten beim Hinzufügen von Zahlen wie Oktal auf etwas anderes schließen. Es ist daher besser, sie zu entfernen, wenn Sie '09' als int behandeln möchten (was ich gerade mache) (z. B. expr 09 + 0 oder strip mit sed).

0
Mike Q

Zur Antwort von Ignacio Vazquez-Abrams hinzugefügt. Dadurch kann das Zeichen + vor der Ganzzahl stehen, und es können beliebig viele Nullen als Dezimalzeichen verwendet werden. Beispielsweise kann +45.00000000 als Ganzzahl betrachtet werden.
$ Muss jedoch so formatiert sein, dass er ein Dezimalzeichen enthält. 45 wird hier nicht als ganze Zahl betrachtet, aber 45.0 ist.

if [[ $1 =~ ^-?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
Elif [[ $1 =~ ^\+?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
else
    echo "no, this is not an integer"
fi
0
JustinMT

oder mit sed:

   test -z $(echo "2000" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # integer

   test -z $(echo "ab12" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # no integer
0
knipwim