Comment incrémenter une variable dans bash?

609

J'ai essayé d'incrémenter une variable numérique en utilisant les deux var=$var+1et var=($var+1)sans succès. La variable est un nombre, bien que bash semble la lire sous forme de chaîne.

Bash version 4.2.45 (1) -release (x86_64-pc-linux-gnu) sur Ubuntu 13.10.

utilisateur221744
la source

Réponses:

948

Il existe plus d’une façon d’incrémenter une variable dans bash, mais ce que vous avez essayé n’est pas correct.

Vous pouvez utiliser par exemple le développement arithmétique :

var=$((var+1))
((var=var+1))
((var+=1))
((var++))

Ou vous pouvez utiliser let:

let "var=var+1"
let "var+=1"
let "var++"

Voir aussi: http://tldp.org/LDP/abs/html/dblparens.html .

Radu Rădeanu
la source
31
ou ((++var))ou ((var=var+1))ou ((var+=1)).
gniourf_gniourf
6
Curieusement, var=0; ((var++))retourne un code d'erreur alors que ce var=0; ((var++)); ((var++))n'est pas le cas. Une idée pourquoi?
Phunehehe
15
@phunehehe Regardez help '(('. La dernière ligne dit:Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise.
Radu Rădeanu
2
Je soupçonne l’évaluation de zéro, 1c’est pourquoi le pourboire de @ gniourf_gniourf inclut, ((++var))mais pas ((var++)).
DreadPirateShawn
4
est-il sécuritaire d'utiliser let var++, sans les guillemets?
wjandrea
161
var=$((var + 1))

L'arithmétique en bash utilise la $((...))syntaxe.

Paul Tanzini
la source
9
Vraiment mieux que la réponse acceptée. En seulement 10% de l’espace disponible, vous avez réussi à fournir suffisamment d’exemples (un exemple suffit - neuf est excessif au point de vous montrer), et vous nous avez fourni suffisamment d’informations pour savoir qu’il ((...))est essentiel d’utiliser l’arithmétique. en bash. Je ne savais pas que, juste en regardant la réponse acceptée - je pensais qu'il y avait un ensemble étrange de règles concernant l'ordre des opérations ou quelque chose qui conduisait à toutes les parenthèses dans la réponse acceptée.
ArtOfWarfare
82

Analyse de performance des différentes options

Merci à la réponse de Radu Rădeanu qui fournit les moyens suivants pour incrémenter une variable dans bash:

var=$((var+1))
((var=var+1))
((var+=1))
((var++))
let "var=var+1"
let "var+=1" 
let "var++"

Il y a aussi d'autres moyens. Par exemple, regardez dans les autres réponses à cette question.

let var++
var=$((var++))
((++var))
{
    declare -i var
    var=var+1
    var+=1
}
{
    i=0
    i=$(expr $i + 1)
}

Avoir autant d'options conduit à ces deux questions:

  1. Y a-t-il une différence de performance entre eux?
  2. Si oui, lequel fonctionne le mieux?

Code de test de performance incrémentiel:

#!/bin/bash

# To focus exclusively on the performance of each type of increment
# statement, we should exclude bash performing while loops from the
# performance measure. So, let's time individual scripts that
# increment $i in their own unique way.

# Declare i as an integer for tests 12 and 13.
echo > t12 'declare -i i; i=i+1'
echo > t13 'declare -i i; i+=1'
# Set i for test 14.
echo > t14 'i=0; i=$(expr $i + 1)'

x=100000
while ((x--)); do
    echo >> t0 'i=$((i+1))'
    echo >> t1 'i=$((i++))'
    echo >> t2 '((i=i+1))'
    echo >> t3 '((i+=1))'
    echo >> t4 '((i++))'
    echo >> t5 '((++i))'
    echo >> t6 'let "i=i+1"'
    echo >> t7 'let "i+=1"'
    echo >> t8 'let "i++"'
    echo >> t9 'let i=i+1'
    echo >> t10 'let i+=1'
    echo >> t11 'let i++'
    echo >> t12 'i=i+1'
    echo >> t13 'i+=1'
    echo >> t14 'i=$(expr $i + 1)'
done

for script in t0 t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 t14; do
    line1="$(head -1 "$script")"
    printf "%-24s" "$line1"
    { time bash "$script"; } |& grep user
    # Since stderr is being piped to grep above, this will confirm
    # there are no errors from running the command:
    eval "$line1"
    rm "$script"
done

Résultats:

i=$((i+1))              user    0m0.992s
i=$((i++))              user    0m0.964s
((i=i+1))               user    0m0.760s
((i+=1))                user    0m0.700s
((i++))                 user    0m0.644s
((++i))                 user    0m0.556s
let "i=i+1"             user    0m1.116s
let "i+=1"              user    0m1.100s
let "i++"               user    0m1.008s
let i=i+1               user    0m0.952s
let i+=1                user    0m1.040s
let i++                 user    0m0.820s
declare -i i; i=i+1     user    0m0.528s
declare -i i; i+=1      user    0m0.492s
i=0; i=$(expr $i + 1)   user    0m5.464s

Conclusion:

Il semble que bash soit le plus rapide à exécuter i+=1quand $iest déclaré comme un entier. letles déclarations semblent particulièrement lentes et exprsont de loin les plus lentes car elles ne sont pas intégrées.

wjandrea
la source
Apparemment, la vitesse est en corrélation avec la longueur de la commande. Je me demande si les commandes appellent les mêmes fonctions.
MatthewRock
18

Il y a aussi ceci:

var=`expr $var + 1`

Prenez note des espaces et notez que « is not »

Bien que les réponses et les commentaires de Radu soient exhaustifs et très utiles, ils sont spécifiques à chaque bash. Je sais que vous avez spécifiquement posé des questions sur bash, mais je pensais vous adresser à la question car je trouvais cette question lorsque je cherchais à faire la même chose en utilisant sh dans busybox sous uCLinux. Ce portable au-delà de bash.

tphelican
la source
1
Vous pouvez également utiliseri=$((i+1))
wjandrea le
Si la substitution de processus $(...)est disponible sur ce shell, je vous recommanderais plutôt de l’utiliser.
Radon Rosborough
7

Il manque une méthode dans toutes les réponses - bc

$ VAR=7    
$ bc <<< "$VAR+2"
9
$ echo $VAR
7
$ VAR=$( bc <<< "$VAR+1" )
$ echo $VAR
8

bcest spécifié par le standard POSIX , il devrait donc être présent sur toutes les versions de systèmes compatibles Ubuntu et POSIX. La <<<redirection pourrait être modifiée pour echo "$VAR" | bcdes raisons de portabilité, mais puisque la question vous pose une question, vous pouvez bashsimplement l'utiliser <<<.

Sergiy Kolodyazhnyy
la source
6

Le code de retour 1problème est présent pour toutes les variantes par défaut ( let, (()), etc.). Cela cause souvent des problèmes, par exemple, dans les scripts qui utilisent set -o errexit. Voici ce que j'utilise pour empêcher le code d'erreur 1des expressions mathématiques évaluées à 0;

math() { (( "$@" )) || true; }

math a = 10, b = 10
math a++, b+=2
math c = a + b
math mod = c % 20
echo $a $b $c $mod
#11 12 23 3
Juve
la source
0

Cela doit être le pire moyen d'accomplir une tâche aussi simple, mais je voulais juste la documenter pour le plaisir, je suppose (c'est le contraire du code golf).

$ var=0
$ echo $var
0
$ var="$(python -c 'print('$var'+1)')"
$ echo $var
1

ou

$ var="$(printf '%s\n' $var'+1' | bc)"
$ echo $var
1

Sérieusement, utilisez l'un des autres choix bien meilleurs ici.

leetbacoon
la source