J'essaie de faire quelque chose d'assez commun: analyser l'entrée utilisateur dans un script shell. Si l'utilisateur a fourni un entier valide, le script fait une chose, et s'il n'est pas valide, il fait autre chose. Le problème, c'est que je n'ai pas trouvé de moyen facile (et raisonnablement élégant) de faire cela - je ne veux pas avoir à le séparer caractère par caractère.
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 tester si une chaîne se compose d'un nombre réel valide en base 10
Et il y a une réponse là-dedans qui parle de regex, mais pour autant que je sache, c'est une fonction disponible en C (entre autres). Pourtant, il avait ce qui ressemblait à une bonne réponse, alors je l'ai essayé avec grep, mais grep ne savait pas quoi en faire. J'ai essayé -P qui sur ma boîte signifie le traiter comme une expression rationnelle PERL - nada. Dash E (-E) ne fonctionnait pas non plus. Et -F non plus.
Juste pour être clair, j'essaye 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 je reçois. (IOW, je m'attendais à ce qu'une entrée non conforme ne renvoie rien pendant qu'une ligne valide se répète.)
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 pourrait-il illustrer comment cela est le plus facile à faire?
Franchement, c'est une lacune de TEST, à 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
la source
[
est ancien compatibletest
;[[
est la nouveauté de Bash, avec plus d'opérations et des règles de cotation différentes. Si vous avez déjà décidé de vous en tenir à Bash, allez-y[[
(c'est vraiment beaucoup plus sympa); si vous avez besoin de portabilité vers d'autres coquilles, évitez[[
complètement.Réponses:
^
indique le début du modèle d'entrée-
est un "-" littéral?
moyens "0 ou 1 de la précédente (-
)"+
moyens "1 ou plusieurs des précédents ([0-9]
)"$
indique la fin du modèle d'entréeAinsi, l'expression régulière correspond à une option
-
(pour le cas des nombres négatifs), suivie d'un ou plusieurs chiffres décimaux.Références :
la source
+
moyens "1 ou plus des précédents", et le$
indique la fin du modèle d'entrée. Ainsi, l'expression régulière correspond à un optionnel-
suivi d'un ou plusieurs chiffres décimaux.[A-z]
ne serait pas seulement vous donnerA-Z
eta-z
mais aussi\
,[
,]
,^
,_
et`
.d[g-i]{2}
pourrait finir non seulement par correspondre,dig
mais aussidish
dans le classement suggéré par cette réponse (où lesh
digraphe est considéré comme un seul caractère, rassemblé aprèsh
).Wow ... il y a tellement de bonnes solutions ici !! De toutes les solutions ci-dessus, je suis d'accord avec @nortally que l'utilisation d'
-eq
un seul liner est la plus cool.J'utilise GNU bash, version
4.1.5
(Debian). J'ai également vérifié cela sur ksh (SunSO 5.10).Voici ma version de vérifier si
$1
est un entier ou non:Cette approche tient également compte des nombres négatifs, dont certaines des autres solutions auront un résultat négatif erroné, et elle autorisera un préfixe de "+" (par exemple +30) qui est évidemment un entier.
Résultats:
La solution fournie par Ignacio Vazquez-Abrams était également très soignée (si vous aimez les regex) après avoir été expliquée. Cependant, il ne gère pas les nombres positifs avec le
+
préfixe, mais il peut facilement être corrigé comme ci-dessous:la source
Retard à 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
case
déclaration.Le découpage de tout signe avant la comparaison ressemble à un petit hack, mais cela rend l'expression de l'instruction case tellement plus simple.
la source
''|*[!0-9]*)
J'aime la solution utilisant le
-eq
test, car il s'agit essentiellement d'un one-liner.Ma propre solution était d'utiliser l'expansion des paramètres pour jeter tous les chiffres et voir s'il restait quelque chose. (J'utilise toujours la version 3.0, je ne l'ai jamais utilisé
[[
ouexpr
avant, mais je suis heureux de les rencontrer.)la source
[ -z "${INPUT_STRING//[0-9]}" ]
solution vraiment sympa!-eq
solution a quelques problèmes; voir ici: stackoverflow.com/a/808740/1858225Pour la portabilité vers pré-Bash 3.1 (lors de l'
=~
introduction du test), utilisezexpr
.expr STRING : REGEX
recherche REGEX ancré au début de STRING, faisant écho au premier groupe (ou durée de la correspondance, si aucune) et renvoyant le succès / échec. C'est une vieille syntaxe 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, bien que je ne me souvienne pas de quelle version.
@(-|)
signifie "-
ou rien",[0-9]
signifie "chiffre" et*([0-9])
signifie "zéro ou plusieurs chiffres".la source
awk
,~
était l'opérateur "regex match". En Perl (tel que copié à partir de C),~
était déjà utilisé pour le "complément de bits", donc ils l'ont utilisé=~
. Cette dernière notation a été copiée dans plusieurs autres langues. (Perl 5.10 et Perl 6 comme~~
plus, mais cela n'a aucun impact ici.) Je suppose que vous pourriez le regarder comme une sorte d'égalité approximative ...Voici encore une autre interprétation (en utilisant uniquement la commande intégrée test et son code de retour):
la source
$()
avecif
. Cela fonctionne:if is_int "$input"
. En outre, le$[]
formulaire est obsolète. Utilisez$(())
plutôt. À l'intérieur de l'un ou l'autre, le signe dollar peut être omis: lesecho "Integer: $((input))"
accolades ne sont nécessaires nulle part dans votre script.test
ne semble pas le soutenir.[[
fait, cependant.[[ 16#aa -eq 16#aa ]] && echo integer
imprime "entier".[[
renvoie des faux positifs pour cette méthode; par exemple[[ f -eq f ]]
réussit. Il faut donc utilisertest
ou[
.Vous pouvez supprimer les non-chiffres et faire une comparaison. Voici un script de démonstration:
Voici à quoi ressemble la sortie du test:
la source
${var//string}
et${var#string}
et dans la section intitulée "Pattern Matching" pour [^ [: digit:]] `(qui est également traitée dansman 7 regex
).match=${match#0*}
ne supprime pas les zéros non significatifs, il supprime au plus un zéro. En utilisant l'extension, cela ne peut être réalisé qu'en utilisantextglob
viamatch=${match##+(0)}
.09
n'est pas un entier si vous considérez qu'un entier n'a pas de zéros non significatifs. Le test est de savoir si l'entrée (09
) est égale à une version filtrée (9
- un entier) et ce n'est pas le cas.Pour moi, la solution la plus simple était d'utiliser la variable à l'intérieur d'une
(())
expression, comme ceci:Bien entendu, cette solution n'est valable que si une valeur de 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 évalueVAR
, comme indiqué dans laArithmetic Evaluation
section de la page de manuel bash (1) . Par conséquent, vous ne devez pas utiliser cette technique lorsque la source du contenu deVAR
est incertaine (et vous ne devez pas non plus utiliser TOUTE autre forme d'expansion variable, bien sûr).la source
if (( var )); then echo "$var is an int."; fi
VAR='a[$(ls)]'; if ((VAR > 0)); then echo "$VAR is a positive integer"; fi
. À ce stade, vous êtes heureux de ne pas avoir saisi de commande diabolique à la placels
. Parce que OP mentionne l'entrée utilisateur , j'espère vraiment que vous ne l'utilisez pas avec l'entrée utilisateur dans le code de production!agent007
ou avec sed:
la source
test -z "${string//[0-9]/}" && echo "integer" || echo "no integer"
... bien que cela reproduise essentiellement la réponse de Dennis Williamsonif [[ -n "$(printf "%s" "${2}" | sed s/[0-9]//g)" ]]; then
Ajout à la réponse d'Ignacio Vazquez-Abrams. Cela permettra au signe + de précéder l'entier et 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.
la source
^[-+]?[0-9]
...?Pour rire, j'ai rapidement élaboré un ensemble de fonctions pour le faire (is_string, is_int, is_float, est une chaîne alpha, ou autre) mais il existe des moyens plus efficaces (moins de code) de le faire:
Faites quelques tests ici, j'ai défini que -44 est un int mais 44- n'est pas etc.:
Production:
REMARQUE: les 0 en tête pourraient déduire autre chose lors de l'ajout de nombres tels que octal, il serait donc préférable de les supprimer si vous avez l'intention de traiter '09' comme un int (ce que je fais) (par exemple
expr 09 + 0
ou supprimer avec sed)la source