Comment puis-je ajouter des nombres dans un script Bash?

567

J'ai ce script Bash et j'ai eu un problème à la ligne 16. Comment puis-je prendre le résultat précédent de la ligne 15 et l'ajouter à la variable de la ligne 16?

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        num= $num + $metab   (line16)
    done
    echo "$num"
 done
pseudo
la source
2
Je pense que vous imprimerez une valeur zéro même en utilisant la réponse suivante. Il y a une astuce, par exemple la sous-station de processus, pour amener la valeur finale de "num intérieur" à "num extérieur".
Scott Chu

Réponses:

977

Pour les entiers :

  • Utilisez l'expansion arithmétique :$((EXPR))

    num=$((num1 + num2))
    num=$(($num1 + $num2))       # Also works
    num=$((num1 + 2 + 3))        # ...
    num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
  • Utilisation de l' exprutilitaire externe . Notez que cela n'est nécessaire que pour les systèmes très anciens.

    num=`expr $num1 + $num2`     # Whitespace for expr is important

Pour virgule flottante :

Bash ne prend pas directement en charge cela, mais il existe quelques outils externes que vous pouvez utiliser:

num=$(awk "BEGIN {print $num1+$num2; exit}")
num=$(python -c "print $num1+$num2")
num=$(perl -e "print $num1+$num2")
num=$(echo $num1 + $num2 | bc)   # Whitespace for echo is important

Vous pouvez également utiliser la notation scientifique (par exemple, 2.5e+2).


Pièges courants :

  • Lorsque vous définissez une variable, vous ne pouvez pas avoir d'espaces de chaque côté de =, sinon cela forcera le shell à interpréter le premier mot comme le nom de l'application à exécuter (par exemple, num=ou num)

    num= 1 num =2

  • bcet exprattendez chaque nombre et opérateur comme un argument séparé, donc les espaces sont importants. Ils ne peuvent pas traiter des arguments comme 3+ +4.

    num=`expr $num1+ $num2`

Karoly Horvath
la source
1
Dans la première réponse, il m'imprime expr: argument non numérique Le second ne fonctionne pas ..
Nick
1
@ Nick: Avez-vous essayé ceci alors que des variables nommées num1et num2existaient et avaient des valeurs entières?
sorpigal
1
Si elles existent, la seule variable est double .. est-ce un problème ??
Nick
2
Oui, c'est un problème :) Remarque: l' $((..))évaluation arithmétique est exécutée en bash. exprest exécuté comme un processus séparé, donc ça va être beaucoup plus lent. utiliser ce dernier sur les systèmes où l'évaluation arithmétique n'est pas prise en charge (sh! = bash)
Karoly Horvath
4
Puisqu'il $((…))est standardisé par POSIX, il devrait devenir de plus en plus rare ce qui exprest nécessaire.
chepner
115

Utilisez l' $(( ))expansion arithmétique.

num=$(( $num + $metab ))

Voir le chapitre 13. Expansion arithmétique pour plus d'informations.

Steve Prentice
la source
2
Étrange. Cela ne fonctionne pas ici lorsque j'essaie de calculer 191.003 + 190.
Sigur
2
Ne fonctionne pas pour les nombres avec un point décimal.
fivedogit
30

Il y a mille et une façons de le faire. Voici une utilisation dc(une calculatrice de bureau à polissage inversé qui prend en charge l'arithmétique de précision illimitée):

dc <<<"$num1 $num2 + p"

Mais si c'est trop bash-y pour vous (ou si la portabilité est importante), vous pourriez dire

echo $num1 $num2 + p | dc

Mais peut-être que vous êtes une de ces personnes qui pensent que RPN est dégueulasse et bizarre; ne t'inquiète pas! bcest là pour vous:

bc <<< "$num1 + $num2"
echo $num1 + $num2 | bc

Cela dit, vous pouvez apporter des améliorations indépendantes à votre script:

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in output-$i-* ; do # 'for' can glob directly, no need to ls
            echo "$j"

             # 'grep' can read files, no need to use 'cat'
            metab=$(grep EndBuffer "$j" | awk '{sum+=$2} END { print sum/120}')
            num=$(( $num + $metab ))
    done
    echo "$num"
done

Comme décrit dans la FAQ Bash 022 , Bash ne prend pas en charge nativement les nombres à virgule flottante. Si vous devez additionner des nombres à virgule flottante, l'utilisation d'un outil externe (comme bcou dc) est requise.

Dans ce cas, la solution serait

num=$(dc <<<"$num $metab + p")

Pour ajouter des nombres à virgule flottante éventuellement accumulés dans num.

sorpigal
la source
1
@Sorpigal Thankssss beaucoup .. Puis-je vous demander autre chose .. comment puis-je imprimer à la fin non pas le num mais le num / 10 ??
Nick
23

En bash,

 num=5
 x=6
 (( num += x ))
 echo $num   # ==> 11

Notez que bash ne peut gérer que l'arithmétique des nombres entiers, donc si votre commande awk renvoie une fraction, alors vous aurez envie de la reconcevoir: voici votre code réécrit un peu pour faire tout le calcul dans awk.

num=0
for ((i=1; i<=2; i++)); do      
    for j in output-$i-*; do
        echo "$j"
        num=$(
           awk -v n="$num" '
               /EndBuffer/ {sum += $2}
               END {print n + (sum/120)}
           ' "$j"
        )
    done
    echo "$num"
done
glenn jackman
la source
20

J'oublie toujours la syntaxe, donc je viens sur Google, mais je ne trouve jamais celle que je connais: P. C'est le plus propre pour moi et le plus fidèle à ce que j'attendais dans d'autres langues.

i=0
((i++))

echo $i;
ssshake
la source
18
 #!/bin/bash
read X
read Y
echo "$(($X+$Y))"
Amarjeet Singh
la source
1
Fonctionne bien sous macOS.
Tmx
15

J'aime aussi beaucoup cette méthode, moins d'encombrement:

count=$[count+1]
unixball
la source
1
Pourquoi ça marche, au fait? Comment appelons-nous cela? Je ne trouve pas de documents à ce sujet.
skyline75489
4
Moins d'encombrement, mais déconseillé.
Camille Goudeseune
7

Une autre POSIXmanière compatible portable de faire bash, qui peut être définie comme une fonction .bashrcpour tous les opérateurs arithmétiques de convenance.

addNumbers () {
    local IFS='+'
    printf "%s\n" "$(( $* ))"
}

et il suffit de l'appeler en ligne de commande comme,

addNumbers 1 2 3 4 5 100
115

L'idée est d'utiliser le séparateur de champ d'entrée (IFS) , une variable spéciale bashutilisée pour le fractionnement des mots après expansion et pour diviser les lignes en mots. La fonction modifie la valeur localement pour utiliser le caractère de séparation de mots comme opérateur de somme+ .

N'oubliez pas que le IFSest modifié localement et ne prend PAS effet sur le IFScomportement par défaut en dehors de l'étendue de la fonction. Un extrait de la man bashpage,

Le shell traite chaque caractère d'IFS comme un délimiteur et divise les résultats des autres extensions en mots sur ces caractères. Si IFS n'est pas défini, ou si sa valeur est exactement la valeur par défaut, les séquences de, et au début et à la fin des résultats des extensions précédentes sont ignorées, et toute séquence de caractères IFS qui ne se trouve pas au début ou à la fin sert à délimiter mots.

Le "$(( $* ))"représente la liste des arguments passés pour être divisés par +et plus tard la valeur de somme est sortie à l'aide de la printffonction. La fonction peut également être étendue pour ajouter de la portée à d'autres opérations arithmétiques.

Inian
la source
2
#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do      
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        let num=num+metab (line 16)
    done
    echo "$num"
done
Milen John Thomas
la source