web-dev-qa-db-fra.com

Comment incrémenter une variable dans bash?

J'ai essayé d'incrémenter une variable numérique en utilisant var=$var+1 et 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.

556
user221744

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 letNAME _:

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

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

865
Radu Rădeanu
var=$((var + 1))

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

135
Paul Tanzini

Analyse de performance des différentes options

Merci à réponse de Radu Rădean 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+=1 lorsque $i est déclaré sous forme d’entier. Les instructions let semblent particulièrement lentes, et expr est de loin le plus lent, car ce n’est pas une commande intégrée.

78
Keith Reynolds

Il y a aussi ceci:

var=`expr $var + 1`

Prenez note des espaces et aussi ` n'est pas '

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.

17
tphelican

Si vous déclarez $var en tant qu'entier, ce que vous avez essayé la première fois fonctionnera réellement:

$ declare -i var=5
$ echo $var
5
$ var=$var+1
$ echo $var
6

Référence: Types de variables, Guide Bash pour les débutants

10
Radon Rosborough

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

bc est spécifié par POSIX standard. Il doit donc être présent sur toutes les versions de systèmes compatibles Ubuntu et POSIX. La redirection <<< peut être modifiée en echo "$VAR" | bc pour la portabilité, mais puisque la question demande à propos de bash - il est correct d'utiliser simplement <<<.

7

Le code de retour numéro 1 est présent pour toutes les variantes par défaut (let, (()), etc.). Cela pose 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 1 d'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
6
Juve

Merde, cela était destiné à un fil de discussion différent - vous ne savez pas comment supprimer ma soumission

#!/bin/bash
myname=$(basename "$0")

# parse sub options
get_opts () {
  rs='' && rc=0 # return string and return code
  while [[ $# -gt 0 ]]; do
    shift
    [[ "$1" =~ -.* ]] && break ||  rs="$rs $1" && rc=$((rc + 1))
  done
  echo "$rs"
}

# help function
help () { cat <<EOP
   $myname: -c cluster [...] -a action [...] -i instance [...]
EOP
}

while [[ $# -gt 0 ]]; do
    case $1 in
        "-a") ACTS="$(get_opts $@)"
           ;;
        "-i") INSTS=$(get_opts $@)
           ;;
        "-c") CLUSTERS=$(get_opts $@)
           ;;
        "-h") help
           ;;
        ?) echo "sorry, I dont do $1"
           exit
           ;;
    esac
    shift
done

exit
0
Neil Verkland