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
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 :
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]+$ ]]
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.
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".
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
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
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
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).
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
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
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)