Tester si la chaîne est un entier valide

j'essaie de faire quelque chose d'assez commun: analyser les entrées des utilisateurs dans un script shell. Si l'Utilisateur a fourni un entier valide, le script fait une chose, et si non valide, il fait autre chose. Le problème est que je n'ai pas trouvé une façon facile (et assez élégante) de le faire - Je ne veux pas avoir à le démonter char par char.

je sais que ça doit être facile mais je ne sais pas comment. Je pourrais le faire dans une douzaine de langues, mais pas BASH!

dans Mes recherches J'ai trouvé ceci:

Expression régulière pour vérifier si une chaîne se compose d'un nombre réel valide en base 10

et il y a une réponse qui parle de regex, mais pour autant que je sache, c'est une fonction disponible en C (entre autres). J'ai essayé avec grep, mais grep ne savait pas quoi en faire. J'ai essayé-P qui sur ma boîte signifie de le traiter comme un Perl regexp-nada. Dash E (-E) ne fonctionne pas non plus. Ni -F.

juste pour être clair, j'essaie quelque chose comme ça, à la recherche de n'importe quelle sortie - de là, je vais pirater le script pour profiter de tout ce que j'obtiens. (OIE, je m'attendais à ce qu'un non-conforme d'entrée ne retourne rien alors qu'une ligne valide répété.)

snafu=$(echo "" | 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

quelqu'un pourrait-il nous expliquer comment cela se fait le plus facilement?

franchement, c'est un court-coming de TEST, dans mon avis. Il devrait avoir un drapeau comme celui-ci

if [ -I "string" ] ;
then
   echo "String is a valid integer."
else
   echo "String is not a valid integer."
fi
94
demandé sur codeforester 2010-02-05 23:55:27

11 réponses

[[ $var =~ ^-?[0-9]+$ ]]
  • Le ^ indique le début de la forme d'entrée
  • le - est un - "" littéral 1519110920"
  • le ? signifie "0 ou 1 du précédent ( - ) "
  • le + signifie "un ou plusieurs des précédents ( [0-9] ) "
  • Le $ indique la fin du modèle d'entrée

de Sorte que le regex correspond à une option - (pour les nombres négatifs), suivie d'un ou plusieurs chiffres décimaux.

Références :

149
répondu Ignacio Vazquez-Abrams 2016-09-19 15:23:21

Wow... il y a tellement de bonnes solutions ici!! De toutes les solutions ci-dessus, je suis d'accord avec @nortally que l'utilisation du -eq une doublure est le plus frais.

j'exécute GNU bash, version 4.1.5 (Debian). J'ai aussi vérifié sur ksh (SunSO 5.10).

voici ma version pour vérifier si est un entier ou non:

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

cette approche tient également compte des nombres négatifs, dont certains les autres solutions auront un résultat négatif défectueux, et il permettra un préfixe de "+" (par exemple +30) qui est évidemment un entier.

Résultats:

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

la solution fournie par Ignacio Vazquez-Abrams était aussi très soignée (si vous aimez regex) après avoir été expliquée. Cependant, il ne traite pas les nombres positifs avec le préfixe + , mais il peut facilement être fixé comme ci-dessous:

[[ $var =~ ^[-+]?[0-9]+$ ]]
53
répondu Peter Ho 2018-06-27 15:35:18

en retard pour la fête. Je suis extrêmement surpris qu'aucune des réponses ne mentionne la solution la plus simple, la plus rapide et la plus portable, la déclaration case .

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

la taille de n'importe quel signe avant la comparaison se sent comme un peu d'un hack, mais cela rend l'expression pour la déclaration de cas tellement plus simple.

23
répondu tripleee 2017-08-02 06:33:52

pour la transférabilité jusqu'au Pré-Bash 3.1 (lorsque le test =~ a été introduit), utiliser 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 recherche REGEX ancré au début de la chaîne, faisant écho au premier groupe (ou à la longueur du match, si aucun) et retournant succès/échec. C'est une ancienne syntaxe regex, d'où l'excès \ . -\? signifie "peut-être - ", [0-9]\+ signifie "un ou plusieurs chiffres", et $ signifie "fin de chaîne".

Bash supporte également les globs étendus, bien que je ne me souvienne pas de la version à partir de laquelle.

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

@(-|) signifie " - ou rien", [0-9] signifie "chiffre", et *([0-9]) signifie "zéro ou plus de chiffres".

9
répondu ephemient 2010-02-05 21:11:58

j'aime la solution qui utilise le test -eq , parce que c'est essentiellement une doublure.

ma propre solution a été d'utiliser l'expansion des paramètres pour jeter tous les chiffres et voir s'il y avait quelque chose qui restait. (J'utilise toujours 3.0, Je n'ai pas utilisé [[ ou expr avant, mais heureux de les rencontrer.)

if [ "${INPUT_STRING//[0-9]}" = "" ]; then
  # yes, natural number
else
  # no, has non-numeral chars
fi
8
répondu nortally 2016-04-28 03:35:42

Voici encore une autre prise sur elle (en utilisant seulement la commande test builtin et son code de retour):

function is_int() { return $(test "$@" -eq "$@" > /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
répondu hans 2010-02-06 08:19:31

vous pouvez rayer les non-chiffres et faire une comparaison. Voici un script de démonstration:

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

voici à quoi ressemble la sortie de test:

44      44      Integer
-44     44      Integer
44-     44      Not integer
4-4     44      Not integer
a4      4       Not integer
4a      4       Not integer
.4      4       Not integer
4.4     44      Not integer
-4.4    44      Not integer
09      9       Not integer
2
répondu Dennis Williamson 2010-02-05 23:00:32

pour moi, la solution la plus simple était d'utiliser la variable à l'intérieur d'une expression (()) , comme suit:

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

bien sûr, cette solution n'est valable que si une valeur de zéro n'a pas de sens pour votre application. Qui est arrivé à être vrai dans mon cas, et c'est beaucoup plus simple que les autres solutions.

comme indiqué dans les commentaires, cela peut vous soumettre à une attaque d'exécution de code: l'opérateur (( )) évalue VAR , comme indiqué dans la section Arithmetic Evaluation de The bash(1) man page . Par conséquent, vous ne devez pas utiliser cette technique lorsque la source du contenu de VAR est incertaine (ni devrait-on utiliser une autre forme d'expansion variable, bien sûr).

2
répondu Trebor Rude 2015-01-07 17:47:23

ou avec 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
répondu knipwim 2013-02-09 11:49:52

ajoutant à la réponse D'Ignacio Vazquez-Abrams. Cela permettra le signe + pour précéder l'entier, et il permettra à tout nombre de zéros que les points décimaux. Par exemple, cela permettra à +45.00000000 d'être considéré comme un entier.

Cependant, 1 $doit être formaté pour contenir un point décimal. 45 n'est pas considéré comme un entier ici, mais 45.0 est.

if [[  =~ ^-?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
elif [[  =~ ^\+?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
else
    echo "no, this is not an integer"
fi
0
répondu JustinMT 2017-07-23 12:41:53

pour rire, je viens à peu près juste de mettre au point un ensemble de fonctions pour faire ceci (is_string, is_int, is_float, is alpha string, ou autre) mais il y a des moyens plus efficaces (moins de code) pour faire ceci:

#!/bin/bash

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

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

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

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

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

Courir à travers quelques tests ici, j'ai défini que -44 est un int, mais 44 - n'est-ce pas 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

sortie:

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 = 

NOTE: le PREMIER 0 pourrait inférer autre chose en ajoutant des nombres tels que octal il serait donc préférable de les rayer si vous avez l'intention de traiter '09' Comme un int (ce que je fais) (par exemple expr 09 + 0 ou rayer avec sed)

0
répondu Mike Q 2018-05-19 16:39:39