Argument chaîne à un entier en bash

74

Essayez de comprendre comment convertir un argument en un entier sur lequel effectuer une arithmétique, puis l’imprimer, par exemple pour addOne.sh:

echo $1 + 1
>>sh addOne.sh 1
prints 1 + 1
utilisateur135986
la source
J'essaie de formater votre question, mais elle reste en quelque sorte incertaine. printf "1 + %s\n" $1 won't do ?
Archemar le

Réponses:

89

En bash, on ne "convertit pas un argument en entier pour effectuer une arithmétique". Dans bash, les variables sont traitées comme des entiers ou des chaînes selon le contexte.

Pour effectuer une opération arithmétique, vous devez appeler l'opérateur d'expansion arithmétique $((...)). Par exemple:

$ a=2
$ echo "$a + 1"
2 + 1
$ echo "$(($a + 1))"
3

ou généralement préféré:

$ echo "$((a + 1))"
3

Vous devez savoir que bash (contrairement à ksh93, zsh ou yash) exécute uniquement des calculs arithmétiques entiers . Si vous avez des nombres à virgule flottante (nombres avec décimales), il existe d'autres outils pour vous aider. Par exemple, utilisez bc:

$ b=3.14
$ echo "$(($b + 1))"
bash: 3.14 + 1: syntax error: invalid arithmetic operator (error token is ".14 + 1")
$ echo "$b + 1" | bc -l
4.14

Ou vous pouvez utiliser un shell avec support arithmétique en virgule flottante au lieu de bash:

zsh> echo $((3.14 + 1))
4.14
John1024
la source
Utiliser $ (()) ou (()) est dangereux si vous ne savez pas ce que sont les chaînes (comme les entrées utilisateur). Considérez ceci: foo = foo ((foo + = 0)) Cela va planter le script alors qu'il essaye d'évaluer récursivement foo. Idem avec: foo = foo foo = $ ((toto + 0))
art
12

Sinon, vous pouvez utiliser expr

Ex:

$ version="0002"
$ expr $version + 0
2
$ expr $version + 1
3
Nam
la source
10

Dans bash, vous pouvez effectuer la conversion de n'importe quoi en entier à l’aide de printf -v :

printf -v int '%d\n' "$1" 2>/dev/null

Les nombres flottants seront convertis en nombres entiers, alors que tout ce qui ne ressemble pas à un nombre sera converti en 0. L'exponentiation sera tronquée au nombre précédent e

Exemple:

$ printf -v int '%d\n' 123.123 2>/dev/null
$ printf '%d\n' "$int"
123
$ printf -v int '%d\n' abc 2>/dev/null
$ printf '%d\n' "$int"
0
$ printf -v int '%d\n' 1e10 2>/dev/null
$ printf '%d\n' "$int"
1
cuonglm
la source
2
Dans d'autres obus, printf -vceci peut être réalisé avec substitution de commande:int="$(printf '%d' 123.123 2>/dev/null)"
Adrian Günter
2
Cela ne fonctionne pas dans bash.
Michael Martinez
@MichaelMartinez êtes-vous sûr? Quelle est la version de bashvous utilisée?
jeudi
GNU bash, version 4.2.46 (1) - release (x86_64-redhat-linux-gnu)
Michael Martinez le
5

Une situation similaire est apparue récemment lors du développement de scripts bash à exécuter dans les environnements Linux et OSX. Le résultat d’une commande sous OSX a renvoyé une chaîne contenant le code de résultat; c'est à dire " 0". Bien sûr, cela n'a pas réussi à tester correctement dans le cas suivant:

if [[ $targetCnt != 0 ]]; then...

La solution consistait à forcer (c'est-à-dire à "convertir") le résultat en un entier similaire à ce que @ John1024 avait répondu ci-dessus, de sorte qu'il fonctionne comme prévu

targetCnt=$(($targetCnt + 0))
if [[ $targetCnt != 0 ]]; then...
JESii
la source
3
==etc dans [[(également [aka test) faire la comparaison de chaîne. Il existe différents opérateurs pour la comparaison arithmétique, par exemple [[ $targetcnt -ne 0 ]]; voir la page de manuel (ou info) sous Expressions conditionnelles. Pour découper des espaces spécifiquement, vous pouvez utiliser [un développement de variable non entre [ $targetcnt == 0 ]guillemets pour obtenir un déchiffrage de mots par défaut (NON effectué dans [[), mais en général, cette approche vous met en danger.
dave_thompson_085
-2

attention aux codes de couleur, même dans trace ( -x), ils n'apparaîtront pas. Ce qui serait révélé, c'est que la chaîne supposée être un nombre est entourée de guillemets, quelle que soit la manière dont vous l'imprimez.

untore
la source
1
J'ai voté contre, car je trouve que votre réponse n'est pas claire. Peut-être pourriez-vous ajouter à quoi le script devrait être corrigé?
Time4Tea
c’était la première réponse à la recherche de bash + entier + chaîne, j’ai donc ajouté une information connexe qui n’y figurait pas parmi les réponses; c’est-à-dire que lorsque les chaînes sont entourées de codes de couleurs, il est difficile de comprendre pourquoi des opérations telles que $((var+var))fail échouent mais si vous echoou les printfdeux vars ils sont les mêmes. Je ne connais pas le correctif, car je n'ai pu le résoudre qu'en désactivant les codes de couleur à la source de la sortie. Pour le repérer dans les journaux de suivi, vous verrez la variable incriminée affectée comme var='0'il se doitvar=0
untore le
Ce que vous pouvez faire, c’est vous assurer que la sortie n’a pas de code couleur sedsi vous ne pouvez pas la désactiver
annuler le