Comment tester si une variable est un nombre en Bash?
je ne peux pas comprendre comment faire un argument transmis à mon script est un nombre ou pas.
Tout ce que je veux faire, c'est quelque chose comme ça:
test *isnumber* && VAR= || echo "need a number"
de l'aide?
30 réponses
Une approche consiste à utiliser une expression régulière, comme suit:
re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
echo "error: Not a number" >&2; exit 1
fi
si la valeur n'est pas nécessairement un entier, envisager de modifier le regex de manière appropriée; par exemple:
^[0-9]+([.][0-9]+)?$
...ou, pour manipuler des nombres avec un signe:
^[+-]?[0-9]+([.][0-9]+)?$
sans bashismes (fonctionne même dans le système V sh),
case $string in
''|*[!0-9]*) echo bad ;;
*) echo good ;;
esac
ceci rejette les chaînes vides et les chaînes contenant des non-chiffres, acceptant tout le reste.
les nombres négatifs ou à virgule flottante ont besoin de travaux supplémentaires. Une idée est d'exclure -
/ .
dans le premier motif" mauvais " et d'ajouter d'autres motifs "mauvais" contenant les utilisations inappropriées d'eux ( ?*-*
/ *.*.*
)
la solution suivante peut également être utilisée dans les shells de base comme Bourne sans avoir besoin d'expressions régulières. Fondamentalement, toute opération d'évaluation de valeur numérique utilisant des non-nombres résultera en une erreur qui sera implicitement considérée comme fausse dans shell:
"$var" -eq "$var"
comme dans:
#!/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 de 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 cacher le message "integer expression expected" que bash imprime au cas où nous n'aurions pas de nombre.
mises en garde (merci aux commentaires ci-dessous):
- les nombres avec des points décimaux sont et non identifiés comme des "nombres" valides
- en utilisant
[[ ]]
au lieu de[ ]
sera toujours évaluer àtrue
- la plupart des coquilles Non-Bash évalueront toujours cette expression comme
true
- le comportement à 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") produit une erreur, 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")
ce test si un nombre est un entier non négatif et est à la fois indépendant de la coquille (c.-à-d. sans bashismes) et utilise seulement shell intégré:
[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";
MAIS EST ERRONÉ .
Comme jilles commenté et suggéré dans sa réponse c'est la bonne façon de le faire en utilisant shell-patterns.
[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";
je suis surpris par les solutions qui analysent directement les formats de nombres dans shell. shell n'est pas bien adapté à cela, étant un DSL pour le contrôle des fichiers et des processus. Il y a de nombreux parsers un peu plus bas, par exemple:
isdecimal() {
# filter octal/hex/ord()
num=$(printf '%s' "" | sed "s/^0*\([1-9]\)//; s/'/^/")
test "$num" && printf '%f' "$num" >/dev/null 2>&1
}
changez '%f ' à n'importe quel format particulier dont vous avez besoin.
Personne n'a suggéré bash extended pattern matching :
[[ == ?(-)+([0-9]) ]] && echo " is an integer"
je regardais les réponses et... réalisé que personne ne pensait à des nombres flottants (avec point)!
en utilisant grep est grand aussi.
-E means extended regexp
- q signifie calme (sans écho)
-qE est la combinaison des deux.
pour tester directement en ligne de commande:
$ echo "32" | grep -E ^\-?[0-9]?\.?[0-9]+$
# answer is: 32
$ echo "3a2" | grep -E ^\-?[0-9]?\.?[0-9]+$
# answer is empty (false)
$ echo ".5" | grep -E ^\-?[0-9]?\.?[0-9]+$
# answer .5
$ echo "3.2" | grep -E ^\-?[0-9]?\.?[0-9]+$
# answer is 3.2
Utilisation dans un script bash:
check=`echo "" | grep -E ^\-?[0-9]*\.?[0-9]+$`
if [ "$check" != '' ]; then
# it IS numeric
echo "Yeap!"
else
# it is NOT numeric.
echo "nooop"
fi
pour faire correspondre juste des entiers, utilisez ceci:
# change check line to:
check=`echo "" | grep -E ^\-?[0-9]+$`
juste une suite à @mary. Mais parce que je n'ai pas assez de rep, ne pourrait pas poster ceci comme un commentaire à ce post. Bref, voici ce que j'ai utilisé:
isnum() { awk -v a="" 'BEGIN {print (a == a + 0)}'; }
La fonction retourne 1 si l'argument est un nombre, sinon retourne "0". Cela fonctionne pour les entiers ainsi que des flotteurs. 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
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
vous pouvez également utiliser les classes de caractères de bash.
if [[ $VAR = *[[:digit:]]* ]]; then
echo "$VAR is numeric"
else
echo "$VAR is not numeric"
fi
les chiffres comprendront l'espace, le point décimal et" e "ou" E " pour la virgule flottante.
mais, si vous spécifiez un nombre hexadécimal de style C, i.e." 0xffff "ou" 0XFFFFFF", [[: digit:]] retourne true. Un peu d'un piège ici, bash vous permet de faire quelque chose comme "0xAZ00" et encore comptez - le comme un chiffre (n'est-ce pas d'une bizarrerie de compilateurs GCC qui vous permettent d'utiliser la notation 0x pour des bases autres que 16???)
vous pourriez vouloir tester pour" 0x "ou" 0X " avant de tester si c'est un numérique si votre entrée est complètement non fiable, sauf si vous voulez 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
vieille question, mais je voulais juste prendre ma solution. Celui-ci n'a pas besoin de tours de coquillage étranges, ou de compter sur quelque chose qui n'a pas été autour pour toujours.
if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then
echo 'is not numeric'
else
echo 'is numeric'
fi
essentiellement, il supprime tous les chiffres de l'entrée, et si vous êtes laissé avec une chaîne de longueur non-zéro alors ce n'était pas un nombre.
je voudrais essayer ceci:
printf "%g" "$var" &> /dev/null
if [[ $? == 0 ]] ; then
echo "$var is a number."
else
echo "$var is not a number."
fi
Note: Ceci reconnaît nan et inf comme nombre.
ne peut pas encore commenter donc je vais ajouter ma propre réponse, qui est une extension à la réponse de glenn jackman en utilisant bash pattern matching.
mon premier besoin était d'identifier les nombres et de distinguer les entiers et les flotteurs. Les définitions de fonction déduites à:
function isInteger() {
[[ == ?(-)+([0-9]) ]]
}
function isFloat() {
[[ == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
j'ai utilisé le test à l'unité (avec shUnit2) pour valider mes modèles ont fonctionné 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}"
done
for 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
}
Notes: le modèle isFloat peut être modifié pour être plus tolérant le point décimal ( @(.,)
) et le symbole E ( @(Ee)
). Mes tests unitaires ne testent que les valeurs qui sont soit integer ou float, mais pas n'importe quelle entrée invalide.
[[ =~ ^-?[0-9]+$ ]] && echo "number"
N'oubliez pas -
pour inclure les nombres négatifs!
j'utilise expr . Elle renvoie une valeur non nulle si vous essayez d'ajouter un zéro à une valeur non numérique:
if expr $number + 0 > /dev/null 2>&1
then
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 crois pas que bc
a tout à fait le même comportement. Ajouter zéro à un non-nombre vous donne zéro et il retourne une valeur de zéro aussi. Peut-être Pouvez-vous combiner bc
et expr
. Utilisez bc
pour ajouter zéro à $number
. Si la réponse est 0
, alors essayez expr
pour vérifier que $number
n'est pas zéro.
test -z "${i//[0-9]}" && echo digits || echo no no no
${i//[0-9]}
remplace tout chiffre de la valeur de $i
par une chaîne vide, voir man -P 'less +/parameter\/' bash
. -z
vérifie si la chaîne résultante a une longueur nulle.
si vous voulez aussi exclure le cas où $i
est vide, vous pouvez utiliser 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
la façon La plus simple est de vérifier s'il contient des caractères 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
une réponse claire a déjà été donnée par @charles Dufy et d'autres. Une solution de bash pure serait d'utiliser 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 radix .
pour fournir un support plus complet des nombres flottants et de la notation scientifique (de nombreux programmes dans C / Fortran ou autrement exporteront flottant de cette façon), un ajout utile à cette ligne serait ce qui suit :
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
permettant ainsi de différencier les types de numéros, si vous cherchez 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
Note: Nous pourrions énumérer les exigences syntaxiques pour la notation décimale et scientifique, une étant d'autoriser 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 de plus de la part D'Aulu's travail, et testé contre de mauvaises chaînes telles que " '-' '-E-1' '0-0'. Voici mes outils regex / substring / expr qui semblent tenir le coup:
parse_num() {
local r=`expr "" : '.*\([.,]\)' 2>/dev/null | tr -d '\n'`
nat='^[+-]?[0-9]+[.,]?$' \
dot="${1%[.,]*}${r}${1##*[.,]}" \
float='^[\+\-]?([.,0-9]+[Ee]?[-+]?|)[0-9]+$'
[[ "" == $dot ]] && [[ "" =~ $float ]] || [[ "" =~ $nat ]]
} # usage: parse_num -123.456
comme j'ai dû toucher avec ce dernièrement et comme karttu appoach avec l'unité test le plus. J'ai révisé le code et ajouté d'autres solutions, essayez-le vous-même pour voir les résultats:
#!/bin/bash
# N={0,1,2,3,...} by syntaxerror
function isNaturalNumber()
{
[[ =~ ^[0-9]+$ ]]
}
# Z={...,-2,-1,0,1,2,...} by karttu
function isInteger()
{
[[ == ?(-)+([0-9]) ]]
}
# Q={...,-½,-¼,0.0,¼,½,...} by karttu
function isFloat()
{
[[ == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
# R={...,-1,-½,-¼,0.E+n,¼,½,1,...}
function isNumber()
{
isNaturalNumber || isInteger || isFloat
}
bools=("TRUE" "FALSE")
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"
false_values="blah meh mooh blah5 67mooh a123bc"
for value in ${int_values} ${float_values} ${false_values}
do
printf " %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value)
printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value)
printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value)
printf "%5s=%-24s\n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value)
done
Donc isNumber() comprend des tirets, des virgules et notation exponentielle et, par conséquent, renvoie la valeur TRUE sur des entiers et des flotteurs où, d'autre part isFloat() retourne FALSE en valeurs entières et isInteger () retourne également FALSE sur floats. Pour votre confort tout en un liners:
isNaturalNumber() { [[ =~ ^[0-9]+$ ]]; }
isInteger() { [[ == ?(-)+([0-9]) ]]; }
isFloat() { [[ == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
isNumber() { isNaturalNumber || isInteger || isFloat ; }
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 "" | sed 's/[0-9]*//g' )"
[ "${TESTVAR}"x = ""x ] && return ${__TRUE} || return ${__FALSE}
}
isNumber
if [ $? -eq ${__TRUE} ] ; then
print "is a number"
fi
j'ai essayé la recette de ultrasawblade car elle me semblait la plus pratique, et je n'ai pas pu la faire fonctionner. En fin de Compte j'ai conçu une autre façon cependant, basée comme d'autres dans la substitution de paramètre, cette fois avec le remplacement de regex:
[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"
il supprime tous les caractères :digit: class dans $var et vérifie si nous sommes laissés avec une chaîne vide, ce qui signifie que l'original était seulement des nombres.
Ce que j'aime c'est son faible encombrement et flexibilité. Sous cette forme, il ne fonctionne que pour les entiers de base 10 non délimités, bien que vous puissiez sûrement utiliser l'appariement de motif pour l'adapter à d'autres besoins.
Quick & Dirty: je sais que ce n'est pas la façon la plus élégante, mais j'ai l'habitude de simplement ajouté un zéro et de tester le résultat. comme ceci:
function isInteger {
[ $((+0)) != 0 ] && echo " is a number" || echo " 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)) renvoie 0 ou bombe 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 [ $((+0)) != 0 ] ; then # isInteger()
echo " backing up files changed in the last days."
OUT="zipIt--day.tgz"
find . -mtime - -type f -print0 | xargs -0 tar cvzf $OUT
return 1
fi
showError $ERROR
}
NOTE: je suppose que je n'ai jamais pensé à vérifier les flotteurs ou les types mixtes qui feront toute la bombe de script... dans mon cas, je ne voulais pas aller plus loin. Je vais jouer avec mrucci solution et Duffy regex-ils semblent les plus robustes dans le cadre de bash...
j'ai trouvé une version assez courte:
function isnum()
{
return `echo "" | awk -F"\n" '{print ("151900920" != "151900920"+0)}'`
}
-
variable à vérifier
number=12345
ounumber=-23234
ounumber=23.167
ounumber=-345.234
-
contrôle numérique ou non numérique
echo $number | grep -E '^-?[0-9]*\.?[0-9]*$' > /dev/null
-
décider des mesures à prendre en se fondant sur le statut de sortie de la ci-dessus
if [ $? -eq 0 ]; then echo "Numeric"; else echo "Non-Numeric"; fi
pour attraper les nombres négatifs:
if [[ == ?(-)+([0-9.]) ]]
then
echo number
else
echo not a number
fi
vous pouvez utiliser " let "aussi 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 quelques réponses dans ce thread.
j'utilise printf comme autres réponses mentionnées, si vous fournissez la chaîne de format" %f "ou" %i " printf fera la vérification pour vous. Plus facile que de réinventer les contrôles, la syntaxe est simple et courte et printf est omniprésent. Si ses un bon choix à mon avis - vous pouvez également utiliser l'idée suivante pour vérifier un certain nombre de choses, ce n'est pas seulement utile pour vérifier les nombres.
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=""
local NUMBER_TYPE=""
local ERROR_MESG=""
local -i PASS=1
local -i FAIL=0
case "${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; }
j'aime la réponse d'Alberto Zaccagni.
if [ "$var" -eq "$var" ] 2>/dev/null; then
conditions préalables importantes: - pas de sous-coquille donné naissance à - pas de RE analyseurs invoquée - la plupart des applications shell n'utilisent pas de nombres réels
mais si $var
est complexe (par exemple un accès au 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?
if [ "$var" -ge 0 ] 2> /dev/null; then ..
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."
supprimer le -\?
dans grep matching pattern si vous n'acceptez pas un nombre entier négatif.
faisant suite à réponse de David W d'Octobre '13, si en utilisant expr
ce pourrait être mieux
test_var=`expr $am_i_numeric \* 0` >/dev/null 2>&1
if [ "$test_var" = "" ]
then
......
si numérique, multiplié par 1 vous donne la même valeur, (y compris les nombres négatifs). Sinon vous obtenez null
que vous pouvez tester pour
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ées par un point.
re="^[0-9]*[.]{0,1}[0-9]*$"
if [[ =~ $re ]]
then
echo "is numeric"
else
echo "Naahh, not numeric"
fi