En passant - l' test && echo "foo" && exit 0 || echo "bar" && exit 1approche que vous utilisez peut avoir des effets secondaires imprévus - si l'écho échoue (la sortie est peut-être vers un FD fermé), le exit 0sera ignoré et le code essaiera ensuite de le faire echo "bar". Si cela échoue également, la &&condition échouera et ne s'exécutera même pas exit 1! L'utilisation de ifdéclarations réelles plutôt que &&/ ||est moins sujette à des effets secondaires inattendus.
Charles Duffy
@CharlesDuffy C'est le genre de pensée vraiment intelligente à laquelle la plupart des gens ne parviennent que lorsqu'ils doivent traquer des insectes velus ...! Je n'ai jamais pensé que l'écho pouvait retourner l'échec.
Camilo Martin
6
Un peu tard pour la fête, mais je connais les dangers dont Charles a parlé, car j'ai dû les traverser il y a un certain temps aussi. Voici donc une ligne 100% infaillible (et bien lisible) pour vous: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }les crochets indiquent que les choses ne doivent PAS être exécutées dans un sous-shell (ce qui serait certainement le cas avec des ()parenthèses utilisées à la place). Avertissement: ne manquez jamais le dernier point-virgule . Sinon, vous risquez bashd'imprimer les messages d'erreur les plus laids (et les plus inutiles) ...
syntaxerror
5
Cela ne fonctionne pas dans Ubuntu, sauf si vous ne supprimez pas les guillemets. Il devrait donc être juste[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Treviño
4
Vous devrez être plus précis sur ce que vous entendez par «nombre» . Un nombre entier? Un nombre à virgule fixe? Notation scientifique ("e")? Existe-t-il une plage requise (par exemple une valeur non signée 64 bits), ou autorisez-vous un nombre pouvant être écrit?
Toby Speight
Réponses:
803
Une approche consiste à utiliser une expression régulière, comme ceci:
re='^[0-9]+$'if![[ $yournumber =~ $re ]];then
echo "error: Not a number">&2; exit 1fi
Si la valeur n'est pas nécessairement un entier, envisagez de modifier la regex de manière appropriée; par exemple:
+1 pour cette approche, mais attention aux décimales, en faisant ce test avec, par exemple, "1.0" ou "1,0" imprime l'erreur: Pas un nombre ".
sourcerebels
15
Je trouve le '' exec> & 2; écho ... '' plutôt idiot. Juste '' echo ...> & 2 ''
lhunath
5
@Ben voulez-vous vraiment gérer plus d'un signe moins? Je le ferais ^-?plutôt que ^-*si vous ne faites pas le travail pour gérer correctement plusieurs inversions.
Charles Duffy
4
@SandraSchlichting Rend toutes les futures sorties vers stderr. Pas vraiment un point ici, où il n'y a qu'un seul écho, mais c'est une habitude que j'ai tendance à prendre pour les cas où les messages d'erreur s'étendent sur plusieurs lignes.
Charles Duffy
29
Je ne sais pas pourquoi l'expression régulière doit être enregistrée dans une variable, mais si c'est pour des raisons de compatibilité, je ne pense pas que ce soit nécessaire. Vous pouvez simplement appliquer directement l'expression: [[ $yournumber =~ ^[0-9]+$ ]].
konsolebox
284
Sans bashismes (fonctionne même dans le système V sh),
case $string in''|*[!0-9]*) echo bad ;;*) echo good ;;esac
Cela rejette les chaînes vides et les chaînes contenant des non-chiffres, acceptant tout le reste.
Les nombres négatifs ou à virgule flottante nécessitent un travail supplémentaire. Une idée est d'exclure -/ .dans le premier "mauvais" modèle et d'ajouter plus de "mauvais" modèles contenant leurs utilisations inappropriées ( ?*-*/ *.*.*)
+1 - c'est un moyen idiomatique et portable de revenir au shell Bourne d'origine, et a un support intégré pour les caractères génériques de style glob. Si vous venez d'un autre langage de programmation, cela a l'air étrange, mais c'est beaucoup plus élégant que de faire face à la fragilité de divers problèmes de citation et à des problèmes de compatibilité sans fin en arrière / latéral avecif test ...
tripleee
6
Vous pouvez changer la première ligne en ${string#-}(qui ne fonctionne pas dans les anciens shell Bourne, mais fonctionne dans n'importe quel shell POSIX) pour accepter les entiers négatifs.
Gilles 'SO- arrête d'être méchant'
4
En outre, cela est facile à étendre aux flottants - il suffit d'ajouter '.' | *.*.*aux motifs interdits et d'ajouter des points aux caractères autorisés. De même, vous pouvez autoriser un signe facultatif avant, bien que je préfère case ${string#[-+]}ignorer simplement le signe.
@Dor Les guillemets ne sont pas nécessaires, car la commande case n'effectue pas de fractionnement de mot ni de génération de chemin sur ce mot. (Cependant, l' expansion dans les modèles de cas peuvent avoir besoin de citer car il détermine si les caractères correspondants de modèle sont littérales ou spéciaux.)
Jilles
193
La solution suivante peut également être utilisée dans des shells basiques tels que Bourne sans avoir besoin d'expressions régulières. Fondamentalement, toute opération d'évaluation de valeur numérique utilisant des non-nombres entraînera une erreur qui sera implicitement considérée comme fausse dans le shell:
"$var"-eq "$var"
un péché:
#!/bin/bash
var=a
if[-n "$var"]&&["$var"-eq "$var"]2>/dev/null;then
echo number
else
echo not a number
fi
Vous pouvez également tester pour $? le code retour de l'opération qui est plus explicite:
[-n "$var"]&&["$var"-eq "$var"]2>/dev/null
if[ $?-ne 0];then
echo $var is not number
fi
La redirection de l'erreur standard est là pour masquer le message "expression entière attendue" que bash affiche au cas où nous n'aurions pas de nombre.
CAVEATS (merci aux commentaires ci-dessous):
Les nombres avec des décimales ne sont pas identifiés comme des "nombres" valides
Utiliser [[ ]]au lieu de [ ]sera toujours évalué àtrue
La plupart des shells non-Bash évalueront toujours cette expression comme true
Le comportement dans Bash n'est pas documenté et peut donc changer sans avertissement
Si la valeur inclut des espaces après le nombre (par exemple "1 a"), une erreur se produit, comme bash: [[: 1 a: syntax error in expression (error token is "a")
Si la valeur est la même que var-name (par exemple i = "i"), produit une erreur, comme bash: [[: i: expression recursion level exceeded (error token is "i")
Je recommanderais toujours cela (mais avec les variables citées pour autoriser les chaînes vides), car le résultat est garanti utilisable sous forme de nombre dans Bash, quoi qu'il arrive.
l0b0
21
Prenez soin d'utiliser des supports simples; [[ a -eq a ]]évalue à vrai (les deux arguments sont convertis à zéro)
Tgr
3
Très agréable! Notez que cela ne fonctionne que pour un entier, pas pour un nombre. J'ai dû vérifier un seul argument qui doit être un entier, donc cela a bien fonctionné:if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
haridsv
6
Je déconseille fortement cette méthode en raison du nombre non négligeable de coques dont la fonction [intégrée évaluera les arguments comme arithmétiques. Cela est vrai à la fois dans ksh93 et mksh. De plus, étant donné que ces deux tableaux prennent en charge, il est facile d'injecter du code. Utilisez plutôt une correspondance de modèle.
ormaaj
3
@AlbertoZaccagni, dans les versions actuelles de bash, ces valeurs sont interprétées avec des règles de contexte numérique uniquement pour [[ ]]mais pas pour [ ]. Cela dit, ce comportement n'est pas spécifié à la fois par la norme POSIX testet dans la propre documentation de bash; les futures versions de bash pourraient modifier le comportement pour qu'il corresponde à ksh sans rompre les promesses comportementales documentées.
Charles Duffy
43
Cela teste si un nombre est un entier non négatif et est à la fois indépendant du shell (c'est-à-dire sans bashismes) et n'utilise que des shell intégrés:
INCORRECT.
Comme cette première réponse (ci-dessous) permet des nombres entiers avec des caractères tant que les premiers ne sont pas premiers dans la variable.
[-z "${num##[0-9]*}"]&& echo "is a number"|| echo "is not a number";
CORRECT .
Comme jilles l'a commenté et suggéré dans sa réponse, c'est la bonne façon de le faire en utilisant des motifs de coquille.
[!-z "${num##*[!0-9]*}"]&& echo "is a number"|| echo "is not a number";
Cela ne fonctionne pas correctement, il accepte toute chaîne commençant par un chiffre. Notez que WORD dans $ {VAR ## WORD} et similaire est un modèle de shell, pas une expression régulière.
jilles
2
Pouvez-vous traduire cette expression en anglais, s'il vous plaît? Je veux vraiment l'utiliser, mais je ne le comprends pas suffisamment pour lui faire confiance, même après avoir lu la page de manuel de bash.
CivFan
2
*[!0-9]*est un modèle qui correspond à toutes les chaînes avec au moins 1 caractère non numérique. ${num##*[!0-9]*}est une "expansion de paramètre" où nous prenons le contenu de la numvariable et supprimons la chaîne la plus longue qui correspond au modèle. Si le résultat de l'expansion des paramètres n'est pas vide ( ! [ -z ${...} ]), alors c'est un nombre car il ne contient aucun caractère non numérique.
mrucci
Malheureusement, cela échoue s'il y a des chiffres dans l'argument, même s'il ne s'agit pas d'un nombre valide. Par exemple "exam1ple" ou "a2b".
Glenn, je supprime shopt -s extglobde votre message (que j'ai surévalué, c'est l'une de mes réponses préférées ici), car dans les constructions conditionnelles, vous pouvez lire: Lorsque les opérateurs ==et !=sont utilisés, la chaîne à droite de l'opérateur est considérée comme un modèle et correspond selon les règles décrites ci-dessous dans Pattern Matching , comme si l' extgloboption shell était activée. J'espère que ça ne vous dérange pas!
gniourf_gniourf
Dans de tels contextes, vous n'avez pas besoin de shopt extglob... c'est une bonne chose à savoir!
gniourf_gniourf
Fonctionne bien pour les entiers simples.
2
@Jdamian: vous avez raison, cela a été ajouté dans Bash 4.1 (qui est sorti fin 2009 ... Bash 3.2 est sorti en 2006 ... c'est maintenant un logiciel ancien, désolé pour ceux qui sont coincés dans le passé). En outre, vous pouvez affirmer que les extglobs ont été introduits dans la version 2.02 (publiée en 1998) et ne fonctionnent pas dans les versions <2.02… Maintenant, votre commentaire ici servira d'avertissement concernant les anciennes versions.
gniourf_gniourf
1
Les variables à l'intérieur [[...]]ne sont pas sujettes au fractionnement de mots ou à l'expansion globale.
glenn jackman
26
Je suis surpris des solutions analysant directement les formats de nombres dans le shell. shell n'est pas bien adapté à cela, étant un DSL pour contrôler les fichiers et les processus. Il existe de nombreux analyseurs de nombres un peu plus bas, par exemple:
isdecimal(){# filter octal/hex/ord()
num=$(printf '%s'"$1"| sed "s/^0*\([1-9]\)/\1/; s/'/^/")
test "$num"&& printf '%f'"$num">/dev/null 2>&1}
Remplacez «% f» par le format particulier dont vous avez besoin.
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
Gilles Quenot
4
@sputnick votre version rompt la sémantique de valeur de retour inhérente (et utile) de la fonction d'origine. Donc, à la place, laissez simplement la fonction telle quelle et utilisez-la:isnumber 23 && echo "this is a number" || echo "not a number"
michael
4
Cela ne devrait-il pas en être de même 2>/dev/nullpour que isnumber "foo"cela ne pollue pas stderr?
gioele
4
Appeler des shells modernes comme bash "une DSL pour contrôler les fichiers et les processus", c'est ignorer qu'ils sont utilisés pour bien plus que cela - certaines distributions ont construit des gestionnaires de packages et des interfaces Web entiers (aussi moche que cela puisse être). Les fichiers batch correspondent à votre description, car même la définition d'une variable est difficile.
Camilo Martin
7
C'est drôle que vous essayiez d'être intelligent en copiant des idiomes d'autres langues. Malheureusement, cela ne fonctionne pas dans les coquilles. Les shells sont très spéciaux, et sans connaissances solides à leur sujet, vous risquez d'écrire du code cassé. Votre code est cassé: isnumber "'a"retournera vrai. Ceci est documenté dans la spécification POSIX où vous lirez: Si le premier caractère est un guillemet simple ou un guillemet double, la valeur doit être la valeur numérique dans le jeu de codes sous-jacent du caractère suivant le guillemet simple ou le guillemet double .
gniourf_gniourf
16
Je regardais les réponses et ... réalisé que personne ne pensait aux nombres FLOAT (avec point)!
L'utilisation de grep est également très bien.
-E signifie regexp étendu
-q signifie silencieux (ne fait pas écho)
-qE est la combinaison des deux.
Pour tester directement dans la ligne de commande:
Les solutions utilisant awk par triple_r et tripleee fonctionnent avec des flotteurs.
Ken Jackson
Merci pour cela et très bon point! Parce que la question est de savoir comment vérifier s'il s'agit d'un nombre et pas seulement d'un entier.
Tanasis
Je te remercie aussi Tanasis! Aidons-nous toujours.
Sergio Abreu
12
Juste un suivi à @mary. Mais parce que je n'ai pas assez de représentants, je ne pouvais pas poster ceci en tant que commentaire. Bref, voici ce que j'ai utilisé:
isnum(){ awk -v a="$1"'BEGIN {print (a == a + 0)}';}
La fonction retournera "1" si l'argument est un nombre, sinon retournera "0". Cela fonctionne aussi bien pour les nombres entiers que pour les flottants. L'utilisation est quelque chose comme:
n=-2.05e+07
res=`isnum "$n"`if["$res"=="1"];then
echo "$n is a number"else
echo "$n is not a number"fi
L'impression d'un nombre est moins utile que la définition d'un code de sortie. 'BEGIN { exit(1-(a==a+0)) }'est un peu difficile à grok mais peut être utilisé dans une fonction qui retourne vrai ou faux tout comme [, grep -q, etc.
tripleee
9
test -z "${i//[0-9]}"&& echo digits || echo no no no
${i//[0-9]}remplace tout chiffre de la valeur de $ipar une chaîne vide, voir man -P 'less +/parameter\/' bash. -zvérifie si la chaîne résultante a une longueur nulle.
si vous souhaitez également exclure le cas lorsqu'il $iest vide, vous pouvez utiliser l'une de ces constructions:
test -n "$i"&& test -z "${i//[0-9]}"&& echo digits || echo not a number
[[-n "$i"&&-z "${i//[0-9]}"]]&& echo digits || echo not a number
Bravo surtout pour la man -P 'less +/parameter\/' bashpartie. Apprendre quelque chose de nouveau chaque jour. :)
David
@sjas Vous pouvez facilement ajouter \-une expression régulière pour résoudre le problème. Utilisez [0-9\-\.\+]pour tenir compte des flotteurs et des numéros signés.
Pour mon problème, je devais seulement m'assurer qu'un utilisateur n'entre pas accidentellement du texte, j'ai donc essayé de le garder simple et lisible
isNumber(){(( $1 ))2>/dev/null
}
Selon la page de manuel, cela fait à peu près ce que je veux
Si la valeur de l'expression est différente de zéro, l'état de retour est 0
Pour éviter les messages d'erreur désagréables pour les chaînes qui "pourraient être des nombres", j'ignore la sortie d'erreur
$ ((2s))
bash:((:2s: value too great for base (error token is "2s")
Cela ressemble plus à isInteger. Mais génial merci!
Naheel
8
Vieille question, mais je voulais juste clouer ma solution. Celui-ci ne nécessite pas d'étranges obus ou ne repose pas sur quelque chose qui n'a pas existé depuis toujours.
if[-n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')"];then
echo 'is not numeric'else
echo 'is numeric'fi
Fondamentalement, il supprime simplement tous les chiffres de l'entrée, et si vous vous retrouvez avec une chaîne de longueur non nulle, ce n'était pas un nombre.
Ou pour les variables avec des retours à la ligne de fin ou quelque chose comme $'0\n\n\n1\n\n\n2\n\n\n3\n'.
gniourf_gniourf
7
Je ne peux pas encore commenter donc j'ajouterai ma propre réponse, qui est une extension de la réponse de Glenn Jackman en utilisant la correspondance de motifs bash.
Mon besoin initial était d'identifier les nombres et de distinguer les entiers et les flottants. Les définitions de fonction déduites:
function isInteger(){[[ ${1}==?(-)+([0-9])]]}function isFloat(){[[ ${1}==?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9]))]]}
J'ai utilisé des tests unitaires (avec shUnit2) pour valider mes modèles qui fonctionnaient comme prévu:
oneTimeSetUp(){
int_values="0 123 -0 -123"
float_values="0.0 0. .0 -0.0 -0. -.0 \
123.456 123. .456 -123.456 -123. -.456
123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"}
testIsIntegerIsFloat(){local value
for value in ${int_values}do
assertTrue "${value} should be tested as integer""isInteger ${value}"
assertFalse "${value} should not be tested as float""isFloat ${value}"donefor value in ${float_values}do
assertTrue "${value} should be tested as float""isFloat ${value}"
assertFalse "${value} should not be tested as integer""isInteger ${value}"done}
Remarques: Le modèle isFloat peut être modifié pour être plus tolérant au point décimal ( @(.,)) et au symbole E ( @(Ee)). Mes tests unitaires testent uniquement les valeurs entières ou flottantes, mais pas toute entrée non valide.
soit un doublon de, ou peut-être mieux adapté comme un commentaire à la réponse de pixelbeat (l'utilisation %fest probablement meilleure de toute façon)
michael
3
Au lieu de vérifier le code d'état précédent, pourquoi ne pas simplement le mettre en iflui-même? C'est ce qui iffait ...if printf "%g" "$var" &> /dev/null; then ...
Camilo Martin
3
Cela a d'autres mises en garde. Il validera la chaîne vide et les chaînes comme 'a.
gniourf_gniourf
6
Une réponse claire a déjà été donnée par @charles Dufy et d'autres. Une solution bash pure utiliserait ce qui suit:
string="-12,345"if[["$string"=~^-?[0-9]+[.,]?[0-9]*$ ]]then
echo $string is a number
else
echo $string is not a number
fi
Bien que pour les nombres réels, il n'est pas obligatoire d'avoir un nombre avant le point de radix .
Pour fournir un support plus complet des nombres flottants et de la notation scientifique (de nombreux programmes en C / Fortran ou bien exporteront float de cette façon), un ajout utile à cette ligne serait le suivant:
string="1.2345E-67"if[["$string"=~^-?[0-9]*[.,]?[0-9]*[eE]?-?[0-9]+$ ]]then
echo $string is a number
else
echo $string is not a number
fi
Menant ainsi à un moyen de différencier les types de numéro, si vous recherchez un type spécifique:
string="-12,345"if[["$string"=~^-?[0-9]+$ ]]then
echo $string is an integer
elif[["$string"=~^-?[0-9]*[.,]?[0-9]*$ ]]then
echo $string is a float
elif[["$string"=~^-?[0-9]*[.,]?[0-9]*[eE]-?[0-9]+$ ]]then
echo $string is a scientific number
else
echo $string is not a number
fi
Remarque: Nous pourrions énumérer les exigences syntaxiques pour la notation décimale et scientifique, l'une étant d'autoriser la virgule comme point radix, ainsi que ".". Nous affirmerions alors qu'il ne doit y avoir qu'un seul point radix. Il peut y avoir deux signes +/- dans un flotteur [Ee]. J'ai appris quelques règles supplémentaires du travail d'Aulu et testé contre de mauvaises chaînes telles que '' '-' '-E-1' '0-0'. Voici mes outils regex / substring / expr qui semblent tenir le coup:
Quelle est la version minimale de bash? Je reçois juste bash: opérateur binaire conditionnel attendu bash: erreur de syntaxe près du jeton inattendu `= ~ '
Paul Hargreaves
1
@PaulHargreaves =~existait au moins depuis bash 3.0.
Gilles 'SO- arrête d'être méchant'
@PaulHargreaves vous avez probablement eu un problème avec votre premier opérande, par exemple trop de guillemets ou similaire
Joshua Clayton
@JoshuaClayton J'ai posé des questions sur la version car elle est très très ancienne bash sur une boîte Solaris 7, que nous avons toujours et qu'elle ne prend pas en charge = ~
Paul Hargreaves
6
Ceci peut être réalisé en utilisant greppour voir si la variable en question correspond à une expression régulière étendue.
Entier de test 1120:
yournumber=1120if echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Production: Valid number.
Test non entier 1120a:
yournumber=1120aif echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Production: Error: not a number.
Explication
Le grep, le -Ecommutateur nous permet d'utiliser l'expression régulière étendue '^[0-9]+$'. Cette expression régulière signifie que la variable ne doit []contenir que les chiffres de 0-9zéro à neuf du ^début à la $fin de la variable et doit avoir au moins +un caractère.
Le grep, le -qcommutateur silencieux désactive toute sortie, qu'il trouve ou non quelque chose.
ifvérifie l'état de sortie de grep. Le statut de sortie 0signifie le succès et toute valeur supérieure signifie une erreur. La grepcommande a un statut de sortie 0si elle trouve une correspondance et 1quand elle ne le fait pas;
Donc, en mettant tout cela ensemble, dans le iftest, nous avons echola variable $yournumberet la |dirige vers celle grepqui avec le -qcommutateur correspond silencieusement à l' -Eexpression d' '^[0-9]+$'expression régulière étendue . Le statut de sortie de grepsera 0s'il grepa trouvé une correspondance avec succès et 1s'il ne l'a pas fait. Si réussi à correspondre, nous echo "Valid number.". Si cela ne correspondait pas, nous echo "Error: not a number.".
Pour flotteurs ou doubles
Nous pouvons simplement changer l'expression régulière de '^[0-9]+$'en '^[0-9]*\.?[0-9]+$'pour flottants ou doubles.
Flotteur d'essai 1120.01:
yournumber=1120.01if echo "$yournumber"| grep -qE '^[0-9]*\.?[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Production: Valid number.
Flotteur d'essai 11.20.01:
yournumber=11.20.01if echo "$yournumber"| grep -qE '^[0-9]*\.?[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Production: Error: not a number.
Pour les négatifs
Pour autoriser des entiers négatifs, changez simplement l'expression régulière de '^[0-9]+$'à '^\-?[0-9]+$'.
Pour autoriser des flottants ou des doubles négatifs, changez simplement l'expression régulière de '^[0-9]*\.?[0-9]+$'à '^\-?[0-9]*\.?[0-9]+$'.
Vous avez raison, @CharlesDuffy. Merci. J'ai nettoyé la réponse.
Joseph Shih
Vous avez raison, @CharlesDuffy. Fixé!
Joseph Shih
1
LGTM; réponse telle que modifiée a mon +1. Les seules choses que je ferais différemment à ce stade ne sont que des questions d'opinion plutôt que de correction (f / e, utiliser à la [-]place de \-et [.]au lieu de \.est un peu plus verbeux, mais cela signifie que vos chaînes n'ont pas à changer si elles ' sont utilisés dans un contexte où les barres obliques inverses sont consommées).
Vous pouvez également utiliser les classes de personnages de bash.
if[[ $VAR =*[[:digit:]]*]];then
echo "$VAR is numeric"else
echo "$VAR is not numeric"fi
Les valeurs numériques incluront l'espace, le point décimal et "e" ou "E" pour le point flottant.
Mais, si vous spécifiez un nombre hexadécimal de style C, c'est-à-dire "0xffff" ou "0XFFFF", [[: digit:]] renvoie true. Un peu un piège ici, bash vous permet de faire quelque chose comme "0xAZ00" et de toujours le compter comme un chiffre (n'est-ce pas d'une bizarrerie bizarre de compilateurs GCC qui vous permettent d'utiliser la notation 0x pour des bases autres que 16 ??? )
Vous voudrez peut-être tester "0x" ou "0X" avant de tester s'il s'agit d'un numérique si votre entrée n'est pas entièrement fiable, sauf si vous souhaitez accepter des nombres hexadécimaux. Cela serait accompli par:
if[[ ${VARIABLE:1:2}="0x"]]||[[ ${VARIABLE:1:2}="0X"]];then echo "$VAR is not numeric";fi
[[ $VAR = *[[:digit:]]* ]]retournera vrai si la variable contient un nombre, pas s'il s'agit d' un entier.
glenn jackman
[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"impressions numeric. Testé en version bash 3.2.25(1)-release.
Jdamian
1
@ultraswadable, votre solution détecte les chaînes contenant au moins un chiffre entouré (ou non) par tout autre caractère. J'ai rétrogradé.
Jdamian
L'approche évidemment correcte est donc d'inverser cela, et d'utiliser[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
eschwartz
@eschwartz, votre solution ne fonctionne pas avec des nombres négatifs
Angel
4
Le moyen le plus simple consiste à vérifier s'il contient des caractères non numériques. Vous remplacez tous les caractères numériques par rien et vérifiez la longueur. S'il y a de la longueur, ce n'est pas un nombre.
if[[!-n ${input//[0-9]/}]];then
echo "Input Is A Number"fi
Pour gérer les nombres négatifs, il faudrait une approche plus compliquée.
Andy
... Ou un signe positif facultatif.
tripleee
@tripleee j'aimerais voir votre approche si vous savez comment le faire.
Andy
4
Comme j'ai dû falsifier ces derniers temps et comme l' approche de karttu avec le test unitaire le plus. J'ai révisé le code et ajouté quelques autres solutions aussi, essayez vous-même pour voir les résultats:
Donc isNumber () inclut des tirets, des virgules et une notation exponentielle et retourne donc TRUE sur les entiers et les flottants alors que d'autre part isFloat () renvoie FALSE sur les valeurs entières et isInteger () retourne également FALSE sur les flottants. Pour votre commodité tout en un:
Personnellement, je supprimerais le functionmot - clé car il ne fait rien d'utile. De plus, je ne suis pas sûr de l'utilité des valeurs de retour. Sauf indication contraire, les fonctions renverront l'état de sortie de la dernière commande, vous n'avez donc pas besoin dereturn rien même.
Tom Fenech
Sympa, en effet les returns sont déroutants et le rendent moins lisible. En utilisantfunction mots-clés ou non est plus une question de saveur personnelle, au moins je les ai supprimés des doublures pour économiser de l'espace. THX.
3ronco
N'oubliez pas que les points-virgules sont nécessaires après les tests pour les versions à une ligne.
Tom Fenech
2
isNumber retournera 'true' sur toute chaîne contenant un nombre.
DrStrangepork
@DrStrangepork En effet, mon tableau false_values manque ce cas. Je vais devoir y réfléchir. Merci pour l'astuce.
3ronco
4
J'utilise expr . Il renvoie un non-zéro si vous essayez d'ajouter un zéro à une valeur non numérique:
if expr --"$number"+0>/dev/null 2>&1then
echo "$number is a number"else
echo "$number isn't a number"fi
Il pourrait être possible d'utiliser bc si vous avez besoin de non-entiers, mais je ne pense pas qu'il bcait le même comportement. L'ajout de zéro à un non-nombre vous donne zéro et renvoie également une valeur de zéro. Vous pouvez peut-être combiner bcet expr. Utilisez bcpour ajouter zéro à $number. Si la réponse est 0, essayez exprde vérifier que ce $numbern'est pas zéro.
C'est plutôt mauvais. Pour le rendre légèrement meilleur, vous devez utiliser expr -- "$number" + 0; mais cela fera toujours semblant 0 isn't a number. De man expr:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
gniourf_gniourf
3
J'aime la réponse d'Alberto Zaccagni.
if["$var"-eq "$var"]2>/dev/null;then
Conditions préalables importantes: - aucun sous-shell généré - aucun analyseur RE invoqué - la plupart des applications shell n'utilisent pas de vrais nombres
Mais si $varest complexe (par exemple un accès à un tableau associatif), et si le nombre sera un entier non négatif (la plupart des cas d'utilisation), alors c'est peut-être plus efficace?
J'utilise printf comme les autres réponses mentionnées, si vous fournissez la chaîne de format "% f" ou "% i" printf fera la vérification pour vous. Plus simple que de réinventer les contrôles, la syntaxe est simple et courte et printf est omniprésent. Donc, c'est un choix décent à mon avis - vous pouvez également utiliser l'idée suivante pour vérifier une gamme de choses, ce n'est pas seulement utile pour vérifier les chiffres.
declare -r CHECK_FLOAT="%f"
declare -r CHECK_INTEGER="%i"## <arg 1> Number - Number to check ## <arg 2> String - Number type to check ## <arg 3> String - Error message function check_number(){local NUMBER="${1}"local NUMBER_TYPE="${2}"local ERROR_MESG="${3}"local-i PASS=1local-i FAIL=0case"${NUMBER_TYPE}"in"${CHECK_FLOAT}")if((! $(printf "${CHECK_FLOAT}""${NUMBER}"&>/dev/random;echo $?)));then
echo "${PASS}"else
echo "${ERROR_MESG}"1>&2
echo "${FAIL}"fi;;"${CHECK_INTEGER}")if((! $(printf "${CHECK_INTEGER}""${NUMBER}"&>/dev/random;echo $?)));then
echo "${PASS}"else
echo "${ERROR_MESG}"1>&2
echo "${FAIL}"fi;;*)
echo "Invalid number type format: ${NUMBER_TYPE} to check_number()."1>&2
echo "${FAIL}";;esac}
>$ var=45
>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }
frapperexpression régulière vsfrapperexpansion des paramètres
Comme la réponse de Charles Duffy fonctionne, mais utilisez uniquementfrapperexpression régulière , et je sais que cela est lent, je voudrais montrer une autre façon, en utilisant uniquementfrapperexpansion des paramètres :
set--"foo bar"
is_num "$1"&& VAR=$1 || echo "need a number"
need a number
set--"+3.141592"
is_num "$1"&& VAR=$1 || echo "need a number"
echo $VAR
+3.141592
if is_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fiIt's a number
if cdIs_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if is_num 3+4;then echo It\'s a number ;else echo Not a number;fiNot a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fiIt's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
OK, c'est bon. Maintenant, combien de temps prendra tout cela (Sur ma framboise pi):
time for i in{1..1000};do is_num +3.14159265;done
real 0m2.476s
user 0m1.235s
sys 0m0.000s
Puis avec des expressions régulières:
time for i in{1..1000};do cdIs_num +3.14159265;done
real 0m4.363s
user 0m2.168s
sys 0m0.000s
Je suis d'accord, de toute façon, je préfère ne pas utiliser l'expression régulière, quand je pourrais utiliser l'expansion des paramètres ... L'abus de RE ralentira le script bash
F. Hauri
Réponse éditée: Pas besoin de extglob(et un peu plus vite)!
F.Hauri
@CharlesDuffy, Sur mon framboise pi, 1000 itérations prendront 2,5sec avec ma version et 4,4sec avec votre version!
F.Hauri
Je ne peux pas discuter avec ça. 👍
Charles Duffy
1
Pour attraper des nombres négatifs:
if[[ $1 ==?(-)+([0-9.])]]then
echo number
else
echo not a number
fi
En outre, cela nécessite que la globalisation étendue soit activée en premier. Il s'agit d'une fonctionnalité uniquement Bash qui est désactivée par défaut.
tripleee
@tripleee étendu globbing est activé automatiquement lorsque vous utilisez == ou! = When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled.gnu.org/software/bash/manual/bashref.html#index-_005b_005b
Badr Elmers
@BadrElmers Merci pour la mise à jour. Cela semble être un nouveau comportement qui n'est pas vrai dans mon Bash 3.2.57 (MacOS Mojave). Je vois que cela fonctionne comme vous le décrivez en 4.4.
tripleee
1
Vous pouvez également utiliser "let" comme ceci:
[~]$ var=1[~]$ let $var && echo "It's a number"|| echo "It's not a number"It\'s a number
[~]$ var=01[~]$ let $var && echo "It's a number"|| echo "It's not a number"It\'s a number
[~]$ var=toto
[~]$ let $var && echo "It's a number"|| echo "It's not a number"It\'s not a number
[~]$
Mais je préfère utiliser l'opérateur "= ~" Bash 3+ comme certaines réponses dans ce fil.
Downvote pour manque d'explication. Comment cela marche-t-il? Il a l'air complexe et fragile, et il n'est pas évident quelles entrées exactement il acceptera. (Par exemple, la suppression d'espaces est-elle absolument nécessaire? Pourquoi? Il dira qu'un nombre avec des espaces intégrés est un nombre valide, ce qui peut ne pas être souhaitable.)
tripleee
1
A fait la même chose ici avec une expression régulière qui teste la partie entière et la partie décimale, séparée par un point.
Pourriez-vous expliquer pourquoi votre réponse est fondamentalement différente des autres anciennes réponses, par exemple, la réponse de Charles Duffy? Eh bien, votre réponse est en fait cassée car elle valide une seule période.
gniourf_gniourf
pas sûr de comprendre la seule période ici ... c'est une ou zéro période attendue ... Mais rien de fondamentalement différent, juste trouvé l'expression régulière plus facile à lire.
Jerome
également utiliser * devrait correspondre à plus de cas du monde réel
Jerome
Le fait est que vous faites correspondre la chaîne vide a=''et la chaîne qui contient un point seulement, a='.'donc votre code est un peu cassé ...
gniourf_gniourf
0
J'utilise ce qui suit (pour les entiers):
## ##### constants#### __TRUE - true (0)## __FALSE - false (1)##
typeset -r __TRUE=0
typeset -r __FALSE=1## --------------------------------------## isNumber## check if a value is an integer ## usage: isNumber testValue ## returns: ${__TRUE} - testValue is a number else not##function isNumber {
typeset TESTVAR="$(echo "$1" | sed 's/[0-9]*//g' )"["${TESTVAR}"x =""x ]&&return ${__TRUE}||return ${__FALSE}}
isNumber $1
if[ $?-eq ${__TRUE}];then
print "is a number"fi
Presque correct (vous acceptez la chaîne vide) mais compliqué au point d'être obscurci.
Gilles 'SO- arrête d'être méchant'
2
Incorrect: vous acceptez -n, etc. (à cause de echo), et vous acceptez des variables avec des retours à la ligne de fin (à cause de $(...)). Et d'ailleurs, printn'est pas une commande shell valide.
gniourf_gniourf
0
J'ai essayé la recette d'ultrasawblade car elle me semblait la plus pratique et je n'ai pas pu la faire fonctionner. En fin de compte, j'ai imaginé une autre manière, basée sur d'autres comme la substitution de paramètres, cette fois avec le remplacement de l'expression régulière:
[["${var//*([[:digit:]])}"]];&& echo "$var is not numeric"|| echo "$var is numeric"
Il supprime chaque caractère: digit: class dans $ var et vérifie s'il nous reste une chaîne vide, ce qui signifie que l'original n'était que des nombres.
Ce que j'aime dans celui-ci, c'est son faible encombrement et sa flexibilité. Sous cette forme, cela ne fonctionne que pour les entiers de base 10 non délimités, bien que vous puissiez sûrement utiliser la correspondance de modèles pour l'adapter à d'autres besoins.
En lisant la solution de mrucci, elle ressemble presque à la mienne, mais en utilisant un remplacement de chaîne régulier au lieu du "style sed". Les deux utilisent les mêmes règles pour la correspondance de motifs et sont, AFAIK, des solutions interchangeables.
ata
sedest POSIX, tandis que votre solution l'est bash. Les deux ont leurs utilisations
v010dya
0
Quick & Dirty: Je sais que ce n'est pas la manière la plus élégante, mais je viens généralement d'ajouter un zéro et de tester le résultat. ainsi:
function isInteger {[ $(($1+0))!=0]&& echo "$1 is a number"|| echo "$1 is not a number"}
x=1; isInteger $x
x="1"; isInteger $x
x="joe"; isInteger $x
x=0x16; isInteger $x
x=-32674; isInteger $x
$ (($ 1 + 0)) retournera 0 ou bombera si $ 1 n'est PAS un entier. par exemple:
function zipIt {# quick zip - unless the 1st parameter is a number
ERROR="not a valid number. "if[ $(($1+0))!=0];then# isInteger($1)
echo " backing up files changed in the last $1 days."
OUT="zipIt-$1-day.tgz"
find .-mtime -$1 -type f -print0 | xargs -0 tar cvzf $OUT
return1fi
showError $ERROR
}
NOTE: Je suppose que je n'ai jamais pensé à vérifier les flotteurs ou les types mixtes qui rendraient la bombe de script entière ... dans mon cas, je ne voulais pas que cela aille plus loin. Je vais jouer avec la solution de mrucci et le regex de Duffy - ils semblent les plus robustes dans le cadre bash ...
Cela accepte des expressions arithmétiques comme 1+1, mais rejette certains entiers positifs avec 0s en tête (car il 08s'agit d'une constante octale invalide).
Gilles 'SO- arrête d'être méchant'
2
Cela a aussi d' autres questions: 0est pas un nombre, et il est soumis à l' injection de code arbitraire, essayez: isInteger 'a[$(ls)]'. Oups.
gniourf_gniourf
1
Et l'expansion de $((...))n'est pas citée, un numérique la IFS=123changera.
test && echo "foo" && exit 0 || echo "bar" && exit 1
approche que vous utilisez peut avoir des effets secondaires imprévus - si l'écho échoue (la sortie est peut-être vers un FD fermé), leexit 0
sera ignoré et le code essaiera ensuite de le faireecho "bar"
. Si cela échoue également, la&&
condition échouera et ne s'exécutera même pasexit 1
! L'utilisation deif
déclarations réelles plutôt que&&
/||
est moins sujette à des effets secondaires inattendus.[[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }
les crochets indiquent que les choses ne doivent PAS être exécutées dans un sous-shell (ce qui serait certainement le cas avec des()
parenthèses utilisées à la place). Avertissement: ne manquez jamais le dernier point-virgule . Sinon, vous risquezbash
d'imprimer les messages d'erreur les plus laids (et les plus inutiles) ...[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Réponses:
Une approche consiste à utiliser une expression régulière, comme ceci:
Si la valeur n'est pas nécessairement un entier, envisagez de modifier la regex de manière appropriée; par exemple:
... ou, pour gérer les nombres avec un signe:
la source
^-?
plutôt que^-*
si vous ne faites pas le travail pour gérer correctement plusieurs inversions.[[ $yournumber =~ ^[0-9]+$ ]]
.Sans bashismes (fonctionne même dans le système V sh),
Cela rejette les chaînes vides et les chaînes contenant des non-chiffres, acceptant tout le reste.
Les nombres négatifs ou à virgule flottante nécessitent un travail supplémentaire. Une idée est d'exclure
-
/.
dans le premier "mauvais" modèle et d'ajouter plus de "mauvais" modèles contenant leurs utilisations inappropriées (?*-*
/*.*.*
)la source
if test ...
${string#-}
(qui ne fonctionne pas dans les anciens shell Bourne, mais fonctionne dans n'importe quel shell POSIX) pour accepter les entiers négatifs.'.' | *.*.*
aux motifs interdits et d'ajouter des points aux caractères autorisés. De même, vous pouvez autoriser un signe facultatif avant, bien que je préfèrecase ${string#[-+]}
ignorer simplement le signe.La solution suivante peut également être utilisée dans des shells basiques tels que Bourne sans avoir besoin d'expressions régulières. Fondamentalement, toute opération d'évaluation de valeur numérique utilisant des non-nombres entraînera une erreur qui sera implicitement considérée comme fausse dans le shell:
un péché:
Vous pouvez également tester pour $? le code retour de l'opération qui est plus explicite:
La redirection de l'erreur standard est là pour masquer le message "expression entière attendue" que bash affiche au cas où nous n'aurions pas de nombre.
CAVEATS (merci aux commentaires ci-dessous):
[[ ]]
au lieu de[ ]
sera toujours évalué àtrue
true
bash: [[: 1 a: syntax error in expression (error token is "a")
bash: [[: i: expression recursion level exceeded (error token is "i")
la source
[[ a -eq a ]]
évalue à vrai (les deux arguments sont convertis à zéro)if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
[
intégrée évaluera les arguments comme arithmétiques. Cela est vrai à la fois dans ksh93 et mksh. De plus, étant donné que ces deux tableaux prennent en charge, il est facile d'injecter du code. Utilisez plutôt une correspondance de modèle.[[ ]]
mais pas pour[ ]
. Cela dit, ce comportement n'est pas spécifié à la fois par la norme POSIXtest
et dans la propre documentation de bash; les futures versions de bash pourraient modifier le comportement pour qu'il corresponde à ksh sans rompre les promesses comportementales documentées.Cela teste si un nombre est un entier non négatif et est à la fois indépendant du shell (c'est-à-dire sans bashismes) et n'utilise que des shell intégrés:
INCORRECT.
Comme cette première réponse (ci-dessous) permet des nombres entiers avec des caractères tant que les premiers ne sont pas premiers dans la variable.
CORRECT .
Comme jilles l'a commenté et suggéré dans sa réponse, c'est la bonne façon de le faire en utilisant des motifs de coquille.
la source
*[!0-9]*
est un modèle qui correspond à toutes les chaînes avec au moins 1 caractère non numérique.${num##*[!0-9]*}
est une "expansion de paramètre" où nous prenons le contenu de lanum
variable et supprimons la chaîne la plus longue qui correspond au modèle. Si le résultat de l'expansion des paramètres n'est pas vide (! [ -z ${...} ]
), alors c'est un nombre car il ne contient aucun caractère non numérique.122s
:-(
Personne n'a suggéré la correspondance étendue des motifs de bash :
la source
shopt -s extglob
de votre message (que j'ai surévalué, c'est l'une de mes réponses préférées ici), car dans les constructions conditionnelles, vous pouvez lire: Lorsque les opérateurs==
et!=
sont utilisés, la chaîne à droite de l'opérateur est considérée comme un modèle et correspond selon les règles décrites ci-dessous dans Pattern Matching , comme si l'extglob
option shell était activée. J'espère que ça ne vous dérange pas!shopt extglob
... c'est une bonne chose à savoir![[...]]
ne sont pas sujettes au fractionnement de mots ou à l'expansion globale.Je suis surpris des solutions analysant directement les formats de nombres dans le shell. shell n'est pas bien adapté à cela, étant un DSL pour contrôler les fichiers et les processus. Il existe de nombreux analyseurs de nombres un peu plus bas, par exemple:
Remplacez «% f» par le format particulier dont vous avez besoin.
la source
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
isnumber 23 && echo "this is a number" || echo "not a number"
2>/dev/null
pour queisnumber "foo"
cela ne pollue pas stderr?isnumber "'a"
retournera vrai. Ceci est documenté dans la spécification POSIX où vous lirez: Si le premier caractère est un guillemet simple ou un guillemet double, la valeur doit être la valeur numérique dans le jeu de codes sous-jacent du caractère suivant le guillemet simple ou le guillemet double .Je regardais les réponses et ... réalisé que personne ne pensait aux nombres FLOAT (avec point)!
L'utilisation de grep est également très bien.
-E signifie regexp étendu
-q signifie silencieux (ne fait pas écho)
-qE est la combinaison des deux.
Pour tester directement dans la ligne de commande:
Utilisation dans un script bash:
Pour faire correspondre les entiers JUST, utilisez ceci:
la source
Juste un suivi à @mary. Mais parce que je n'ai pas assez de représentants, je ne pouvais pas poster ceci en tant que commentaire. Bref, voici ce que j'ai utilisé:
La fonction retournera "1" si l'argument est un nombre, sinon retournera "0". Cela fonctionne aussi bien pour les nombres entiers que pour les flottants. L'utilisation est quelque chose comme:
la source
'BEGIN { exit(1-(a==a+0)) }'
est un peu difficile à grok mais peut être utilisé dans une fonction qui retourne vrai ou faux tout comme[
,grep -q
, etc.${i//[0-9]}
remplace tout chiffre de la valeur de$i
par une chaîne vide, voirman -P 'less +/parameter\/' bash
.-z
vérifie si la chaîne résultante a une longueur nulle.si vous souhaitez également exclure le cas lorsqu'il
$i
est vide, vous pouvez utiliser l'une de ces constructions:la source
man -P 'less +/parameter\/' bash
partie. Apprendre quelque chose de nouveau chaque jour. :)\-
une expression régulière pour résoudre le problème. Utilisez[0-9\-\.\+]
pour tenir compte des flotteurs et des numéros signés.echo $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
Pour mon problème, je devais seulement m'assurer qu'un utilisateur n'entre pas accidentellement du texte, j'ai donc essayé de le garder simple et lisible
Selon la page de manuel, cela fait à peu près ce que je veux
Pour éviter les messages d'erreur désagréables pour les chaînes qui "pourraient être des nombres", j'ignore la sortie d'erreur
la source
Vieille question, mais je voulais juste clouer ma solution. Celui-ci ne nécessite pas d'étranges obus ou ne repose pas sur quelque chose qui n'a pas existé depuis toujours.
Fondamentalement, il supprime simplement tous les chiffres de l'entrée, et si vous vous retrouvez avec une chaîne de longueur non nulle, ce n'était pas un nombre.
la source
var
.$'0\n\n\n1\n\n\n2\n\n\n3\n'
.Je ne peux pas encore commenter donc j'ajouterai ma propre réponse, qui est une extension de la réponse de Glenn Jackman en utilisant la correspondance de motifs bash.
Mon besoin initial était d'identifier les nombres et de distinguer les entiers et les flottants. Les définitions de fonction déduites:
J'ai utilisé des tests unitaires (avec shUnit2) pour valider mes modèles qui fonctionnaient comme prévu:
Remarques: Le modèle isFloat peut être modifié pour être plus tolérant au point décimal (
@(.,)
) et au symbole E (@(Ee)
). Mes tests unitaires testent uniquement les valeurs entières ou flottantes, mais pas toute entrée non valide.la source
J'essaierais ceci:
Remarque: cela reconnaît nan et inf comme nombre.
la source
%f
est probablement meilleure de toute façon)if
lui-même? C'est ce quiif
fait ...if printf "%g" "$var" &> /dev/null; then ...
'a
.Une réponse claire a déjà été donnée par @charles Dufy et d'autres. Une solution bash pure utiliserait ce qui suit:
Bien que pour les nombres réels, il n'est pas obligatoire d'avoir un nombre avant le point de radix .
Pour fournir un support plus complet des nombres flottants et de la notation scientifique (de nombreux programmes en C / Fortran ou bien exporteront float de cette façon), un ajout utile à cette ligne serait le suivant:
Menant ainsi à un moyen de différencier les types de numéro, si vous recherchez un type spécifique:
Remarque: Nous pourrions énumérer les exigences syntaxiques pour la notation décimale et scientifique, l'une étant d'autoriser la virgule comme point radix, ainsi que ".". Nous affirmerions alors qu'il ne doit y avoir qu'un seul point radix. Il peut y avoir deux signes +/- dans un flotteur [Ee]. J'ai appris quelques règles supplémentaires du travail d'Aulu et testé contre de mauvaises chaînes telles que '' '-' '-E-1' '0-0'. Voici mes outils regex / substring / expr qui semblent tenir le coup:
la source
N'oubliez pas
-
d'inclure des nombres négatifs!la source
=~
existait au moins depuis bash 3.0.Ceci peut être réalisé en utilisant
grep
pour voir si la variable en question correspond à une expression régulière étendue.Entier de test
1120
:Production:
Valid number.
Test non entier
1120a
:Production:
Error: not a number.
Explication
grep
, le-E
commutateur nous permet d'utiliser l'expression régulière étendue'^[0-9]+$'
. Cette expression régulière signifie que la variable ne doit[]
contenir que les chiffres de0-9
zéro à neuf du^
début à la$
fin de la variable et doit avoir au moins+
un caractère.grep
, le-q
commutateur silencieux désactive toute sortie, qu'il trouve ou non quelque chose.if
vérifie l'état de sortie degrep
. Le statut de sortie0
signifie le succès et toute valeur supérieure signifie une erreur. Lagrep
commande a un statut de sortie0
si elle trouve une correspondance et1
quand elle ne le fait pas;Donc, en mettant tout cela ensemble, dans le
if
test, nous avonsecho
la variable$yournumber
et la|
dirige vers cellegrep
qui avec le-q
commutateur correspond silencieusement à l'-E
expression d''^[0-9]+$'
expression régulière étendue . Le statut de sortie degrep
sera0
s'ilgrep
a trouvé une correspondance avec succès et1
s'il ne l'a pas fait. Si réussi à correspondre, nousecho "Valid number."
. Si cela ne correspondait pas, nousecho "Error: not a number."
.Pour flotteurs ou doubles
Nous pouvons simplement changer l'expression régulière de
'^[0-9]+$'
en'^[0-9]*\.?[0-9]+$'
pour flottants ou doubles.Flotteur d'essai
1120.01
:Production:
Valid number.
Flotteur d'essai
11.20.01
:Production:
Error: not a number.
Pour les négatifs
Pour autoriser des entiers négatifs, changez simplement l'expression régulière de
'^[0-9]+$'
à'^\-?[0-9]+$'
.Pour autoriser des flottants ou des doubles négatifs, changez simplement l'expression régulière de
'^[0-9]*\.?[0-9]+$'
à'^\-?[0-9]*\.?[0-9]+$'
.la source
[-]
place de\-
et[.]
au lieu de\.
est un peu plus verbeux, mais cela signifie que vos chaînes n'ont pas à changer si elles ' sont utilisés dans un contexte où les barres obliques inverses sont consommées).http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
Vous pouvez également utiliser les classes de personnages de bash.
Les valeurs numériques incluront l'espace, le point décimal et "e" ou "E" pour le point flottant.
Mais, si vous spécifiez un nombre hexadécimal de style C, c'est-à-dire "0xffff" ou "0XFFFF", [[: digit:]] renvoie true. Un peu un piège ici, bash vous permet de faire quelque chose comme "0xAZ00" et de toujours le compter comme un chiffre (n'est-ce pas d'une bizarrerie bizarre de compilateurs GCC qui vous permettent d'utiliser la notation 0x pour des bases autres que 16 ??? )
Vous voudrez peut-être tester "0x" ou "0X" avant de tester s'il s'agit d'un numérique si votre entrée n'est pas entièrement fiable, sauf si vous souhaitez accepter des nombres hexadécimaux. Cela serait accompli par:
la source
[[ $VAR = *[[:digit:]]* ]]
retournera vrai si la variable contient un nombre, pas s'il s'agit d' un entier.[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"
impressionsnumeric
. Testé en version bash3.2.25(1)-release
.[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
Le moyen le plus simple consiste à vérifier s'il contient des caractères non numériques. Vous remplacez tous les caractères numériques par rien et vérifiez la longueur. S'il y a de la longueur, ce n'est pas un nombre.
la source
Comme j'ai dû falsifier ces derniers temps et comme l' approche de karttu avec le test unitaire le plus. J'ai révisé le code et ajouté quelques autres solutions aussi, essayez vous-même pour voir les résultats:
Donc isNumber () inclut des tirets, des virgules et une notation exponentielle et retourne donc TRUE sur les entiers et les flottants alors que d'autre part isFloat () renvoie FALSE sur les valeurs entières et isInteger () retourne également FALSE sur les flottants. Pour votre commodité tout en un:
la source
function
mot - clé car il ne fait rien d'utile. De plus, je ne suis pas sûr de l'utilité des valeurs de retour. Sauf indication contraire, les fonctions renverront l'état de sortie de la dernière commande, vous n'avez donc pas besoin dereturn
rien même.return
s sont déroutants et le rendent moins lisible. En utilisantfunction
mots-clés ou non est plus une question de saveur personnelle, au moins je les ai supprimés des doublures pour économiser de l'espace. THX.J'utilise expr . Il renvoie un non-zéro si vous essayez d'ajouter un zéro à une valeur non numérique:
Il pourrait être possible d'utiliser bc si vous avez besoin de non-entiers, mais je ne pense pas qu'il
bc
ait le même comportement. L'ajout de zéro à un non-nombre vous donne zéro et renvoie également une valeur de zéro. Vous pouvez peut-être combinerbc
etexpr
. Utilisezbc
pour ajouter zéro à$number
. Si la réponse est0
, essayezexpr
de vérifier que ce$number
n'est pas zéro.la source
expr -- "$number" + 0
; mais cela fera toujours semblant0 isn't a number
. Deman expr
:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
J'aime la réponse d'Alberto Zaccagni.
Conditions préalables importantes: - aucun sous-shell généré - aucun analyseur RE invoqué - la plupart des applications shell n'utilisent pas de vrais nombres
Mais si
$var
est complexe (par exemple un accès à un tableau associatif), et si le nombre sera un entier non négatif (la plupart des cas d'utilisation), alors c'est peut-être plus efficace?la source
J'utilise printf comme les autres réponses mentionnées, si vous fournissez la chaîne de format "% f" ou "% i" printf fera la vérification pour vous. Plus simple que de réinventer les contrôles, la syntaxe est simple et courte et printf est omniprésent. Donc, c'est un choix décent à mon avis - vous pouvez également utiliser l'idée suivante pour vérifier une gamme de choses, ce n'est pas seulement utile pour vérifier les chiffres.
>$ var=45
>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }
la source
frapper expression régulière vsfrapper expansion des paramètres
Comme la réponse de Charles Duffy fonctionne, mais utilisez uniquementfrapperexpression régulière , et je sais que cela est lent, je voudrais montrer une autre façon, en utilisant uniquementfrapperexpansion des paramètres :
Pour les entiers positifs uniquement:
Pour les entiers:
Puis pour flotter:
Pour correspondre à la demande initiale:
Maintenant une petite comparaison:
Il y a un même contrôle basé sur la réponse de Charles Duffy :
Quelques tests:
OK, c'est bon. Maintenant, combien de temps prendra tout cela (Sur ma framboise pi):
Puis avec des expressions régulières:
la source
extglob
(et un peu plus vite)!Pour attraper des nombres négatifs:
la source
When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled.
gnu.org/software/bash/manual/bashref.html#index-_005b_005bVous pouvez également utiliser "let" comme ceci:
Mais je préfère utiliser l'opérateur "= ~" Bash 3+ comme certaines réponses dans ce fil.
la source
Supprimez le
-\?
modèle de correspondance in grep si vous n'acceptez pas un entier négatif.la source
A fait la même chose ici avec une expression régulière qui teste la partie entière et la partie décimale, séparée par un point.
la source
.
a=''
et la chaîne qui contient un point seulement,a='.'
donc votre code est un peu cassé ...J'utilise ce qui suit (pour les entiers):
la source
-n
, etc. (à cause deecho
), et vous acceptez des variables avec des retours à la ligne de fin (à cause de$(...)
). Et d'ailleurs,print
n'est pas une commande shell valide.J'ai essayé la recette d'ultrasawblade car elle me semblait la plus pratique et je n'ai pas pu la faire fonctionner. En fin de compte, j'ai imaginé une autre manière, basée sur d'autres comme la substitution de paramètres, cette fois avec le remplacement de l'expression régulière:
Il supprime chaque caractère: digit: class dans $ var et vérifie s'il nous reste une chaîne vide, ce qui signifie que l'original n'était que des nombres.
Ce que j'aime dans celui-ci, c'est son faible encombrement et sa flexibilité. Sous cette forme, cela ne fonctionne que pour les entiers de base 10 non délimités, bien que vous puissiez sûrement utiliser la correspondance de modèles pour l'adapter à d'autres besoins.
la source
sed
est POSIX, tandis que votre solution l'estbash
. Les deux ont leurs utilisationsQuick & Dirty: Je sais que ce n'est pas la manière la plus élégante, mais je viens généralement d'ajouter un zéro et de tester le résultat. ainsi:
$ (($ 1 + 0)) retournera 0 ou bombera si $ 1 n'est PAS un entier. par exemple:
NOTE: Je suppose que je n'ai jamais pensé à vérifier les flotteurs ou les types mixtes qui rendraient la bombe de script entière ... dans mon cas, je ne voulais pas que cela aille plus loin. Je vais jouer avec la solution de mrucci et le regex de Duffy - ils semblent les plus robustes dans le cadre bash ...
la source
1+1
, mais rejette certains entiers positifs avec0
s en tête (car il08
s'agit d'une constante octale invalide).0
est pas un nombre, et il est soumis à l' injection de code arbitraire, essayez:isInteger 'a[$(ls)]'
. Oups.$((...))
n'est pas citée, un numérique laIFS=123
changera.