Question Comment tester si une variable est un nombre dans Bash?


Je n'arrive pas à comprendre comment je m'assure qu'un argument passé à mon script est un nombre ou non.

Tout ce que je veux faire est quelque chose comme ceci:

test *isnumber* $1 && VAR=$1 || echo "need a number"

De l'aide?


446
2018-04-30 13:30


origine


Réponses:


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 1
fi

Si la valeur n'est pas nécessairement un nombre entier, pensez à modifier l'expression rationnelle de manière appropriée; par exemple:

^[0-9]+([.][0-9]+)?$

... ou, pour gérer des nombres négatifs:

^-?[0-9]+([.][0-9]+)?$

596
2018-04-30 13:32



Sans les bashismes (fonctionne même dans le System 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 ajouter plus de "mauvais" modèles contenant les utilisations inappropriées d'entre eux (?*-* / *.*.*)


213
2017-10-16 22:56



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 valeurs numériques utilisant des non-nombres entraînera une erreur qui sera implicitement considérée comme fausse dans shell:

"$var" -eq "$var"

un péché:

#!/bin/bash

var=a

if [ "$var" -eq "$var" ] 2>/dev/null; then
  echo number
else
  echo not a number
fi

Vous pouvez également tester $? le code retour de l'opération qui est plus explicite:

"$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 imprime au cas où nous n'aurions pas de numéro.

CAVEATS (merci aux commentaires ci-dessous):

  • Les nombres avec les points décimaux sont ne pas identifié comme "numéros" valides
  • En utilisant [[ ]] au lieu de [ ] évaluera toujours à true
  • La plupart des shells non-Bash évalueront toujours cette expression true
  • Le comportement dans Bash est non documenté et peut donc changer sans avertissement
  • Si la valeur inclut des espaces après le numéro (par exemple "1 a"), une erreur se produit, par exemple 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"), génère une erreur, par exemple bash: [[: i: expression recursion level exceeded (error token is "i")

140
2018-04-30 19:55



Ceci teste si un nombre est un nombre entier non négatif et est à la fois indépendant de la coque (c'est-à-dire sans les bashismes) et utilise uniquement les inserts de shell:

[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";

MAIS EST MAUVAIS.
Comme jilles a commenté et suggéré dans sa réponse C'est la bonne façon de le faire en utilisant des modèles de shell.

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";

32
2018-04-24 14:19



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 contrôler les fichiers et les processus. Il y a beaucoup de parseurs 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
}

Changez '% f' selon le format particulier dont vous avez besoin.


27
2017-07-14 00:01



Personne n'a suggéré de bash correspondance de motif étendue:

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"

25
2017-10-26 14:52



Juste un suivi de @mary. Mais parce que je n'ai pas assez de rep, je ne peux pas poster ceci comme commentaire à ce poste. De toute façon, 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 pour les entiers ainsi que 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

11
2018-02-28 21:36