Teste si la chaîne est un entier valide

J’essaie de faire quelque chose d’assez commun: parsingr les entrées de l’utilisateur dans un script shell. Si l’utilisateur fournit un entier valide, le script ne fait qu’une chose et, s’il n’est pas valide, il fait autre chose. Le problème est que je n’ai pas trouvé de moyen facile (et assez élégant) de le faire – je ne veux pas avoir à le choisir séparément 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 est constituée d’un nombre réel valide dans la base 10

Et il y a une réponse qui parle de regex, mais pour autant que je sache, c’est une fonction disponible dans C (entre autres). Pourtant, il y avait ce qui semblait être une excellente réponse, alors je l’ai essayé avec grep, mais grep ne savait pas quoi faire avec. J’ai essayé – P qui sur ma boite signifie le traiter comme une expression PERL. Dash E (-E) n’a pas fonctionné non plus. Et pas plus -F.

Juste pour être clair, j’essaie quelque chose comme ça, à la recherche de n’importe quelle sortie – à partir de là, je vais pirater le script pour profiter de tout ce que j’ai. (IOW, je m’attendais à ce qu’une entrée non conforme ne retourne rien alors qu’une ligne valide est répétée.)

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 

Quelqu’un voudrait-il illustrer comment cela se fait le plus facilement?

Franchement, il s’agit d’une pénurie de TEST, à mon avis. Il devrait avoir un drapeau comme celui-ci

 if [ -I "ssortingng" ] ; then echo "Ssortingng is a valid integer." else echo "Ssortingng is not a valid integer." fi 

 [[ $var =~ ^-?[0-9]+$ ]] 
  • Le ^ indique le début du motif d’entrée
  • Le - est un littéral “-”
  • Le ? signifie “0 ou 1 du précédent ( - )”
  • Le + signifie “1 ou plus du précédent ( [0-9] )”
  • Le $ indique la fin du pattern d’entrée

Le regex correspond donc à un optionnel - (pour les nombres négatifs), suivi par un ou plusieurs chiffres décimaux.

Références :

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 one liner est la plus cool.

Je cours GNU bash, version 4.1.5 (Debian). Je l’ai aussi vérifié sur ksh (SunSO 5.10).

Voici ma version de vérification si $1 est un entier ou non:

 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 

Cette approche prend également en compte les nombres négatifs, que certaines des autres solutions auront un résultat négatif erroné, et autorisera un préfixe “+” (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 également très soignée (si vous aimez les regex) après qu’elle ait été expliquée. Cependant, il ne gère pas les nombres positifs avec le préfixe + , mais il peut facilement être corrigé comme suit:

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

Latecomer à la fête ici. 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 de case .

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

Le découpage de tout signe avant la comparaison ressemble à un hack, mais cela simplifie beaucoup l’expression de la déclaration de cas.

Pour que la portabilité pré-Bash 3.1 (lorsque le test =~ a été introduit), utilisez expr .

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

expr STRING : REGEX recherche REGEX ancré au début de STRING, faisant écho au premier groupe (ou à la longueur de la correspondance, s’il n’y en a pas) et renvoyant succès / échec. C’est l’ancienne syntaxe de regex, d’où l’excès \ . -\? signifie “peut - être - “, [0-9]\+ signifie “un ou plusieurs chiffres”, et $ signifie “fin de chaîne”.

Bash prend également en charge les globes étendus, même si je ne me souviens plus de quelle version.

 shopt -s extglob case "$ssortingng" of @(-|)[0-9]*([0-9])) echo "Ssortingng is a valid integer." ;; *) echo "Ssortingng is not a valid integer." ;; esac # equivalently, [[ $ssortingng = @(-|)[0-9]*([0-9])) ]] 

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

J’aime la solution utilisant le test -eq , car il s’agit essentiellement d’un one-liner.

Ma propre solution consistait à utiliser l’extension des parameters pour jeter tous les chiffres et voir s’il restait quelque chose. (J’utilise toujours la version 3.0, je n’ai pas utilisé [[ ou expr avant, mais je suis heureux de les rencontrer.]

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

Voici encore une autre idée (en utilisant uniquement la commande intégrée de test 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 

Vous pouvez supprimer les 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:]]} # ssortingp non-digits match=${match#0*} # ssortingp leading zeros echo -en "$num\t$match\t" case $num in $match|-$match) echo "Integer";; *) echo "Not integer";; esac done 

Voici à quoi ressemble le résultat du test:

 44 44 Entier
 -44 44 Entier
 44- 44 Non entier
 4-4 44 Non entier
 a4 4 Non entier
 4a 4 Non entier
 .4 4 Non entier
 4.4 44 Non entier
 -4,4 44 Non entier
 09 9 Non entier

Pour moi, la solution la plus simple consistait à utiliser la variable dans une expression (()) , comme suit:

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

Bien entendu, cette solution n’est valide que si la valeur zéro n’a pas de sens pour votre application. Cela s’est avéré être vrai dans mon cas, et c’est beaucoup plus simple que les autres solutions.

Comme indiqué dans les commentaires, cela peut vous exposer à une attaque d’exécution de code: l’opérateur (( )) évalue la VAR , comme indiqué dans la section Arithmetic Evaluation de la page de manuel bash (1) . Par conséquent, vous ne devez pas utiliser cette technique lorsque la source du contenu de VAR est incertaine (vous ne devez évidemment utiliser aucune autre forme d’extension de variable).

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 

Ajout à la réponse d’Ignacio Vazquez-Abrams. Cela permettra au signe + de précéder l’entier et il autorisera n’importe quel nombre de zéros comme 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 l’est.

 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 

Pour les rires, je me suis mis à élaborer rapidement un ensemble de fonctions pour ce faire (is_ssortingng, is_int, is_float, est une chaîne alpha ou autre), mais il existe des moyens plus efficaces (moins de code) pour y parvenir:

 #!/bin/bash function ssortingndex() { 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 ! ssortingndex "${1}" "-" ; then false return fi tmp=$(echo "${1}" | sed 's/[^az. ]*//g') if [[ $tmp =~ "." ]] ; then #echo "FLOAT (${1}) tmp=$tmp" true else #echo "NOT FLOAT (${1}) tmp=$tmp" false fi } function is_ssortingct_ssortingng() { 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_ssortingng() { if is_empty "${1}" || is_int "${1}" || is_float "${1}" || is_ssortingct_ssortingng "${1}" ;then false return fi if [ ! -z "${1}" ] ;then true return fi false } function is_empty() { if [ -z "${1// }" ] ;then true else false fi } 

Exécuter quelques tests ici, j’ai défini que -44 est un int mais 44- n’est 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_ssortingng "$num" ; then echo "STRING = $num" elif is_ssortingct_ssortingng "$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: les 0 principaux peuvent déduire quelque chose d’autre lors de l’ajout de nombres tels que octal, il serait donc préférable de les supprimer si vous souhaitez traiter «09» comme un int (par exemple expr 09 + 0 ou ssortingp with sed) )