Wie inkrementiere ich eine Variable in bash?

611

Ich habe versucht, eine numerische Variable mit beiden var=$var+1und var=($var+1)ohne Erfolg zu erhöhen. Die Variable ist eine Zahl, obwohl Bash sie als Zeichenfolge zu lesen scheint.

Bash Version 4.2.45 (1) -Release (x86_64-pc-linux-gnu) unter Ubuntu 13.10.

user221744
quelle

Antworten:

951

Es gibt mehr als eine Möglichkeit, eine Variable in bash zu erhöhen, aber was Sie versucht haben, ist nicht korrekt.

Sie können beispielsweise die arithmetische Erweiterung verwenden :

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

Oder Sie können verwenden let:

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

Siehe auch: http://tldp.org/LDP/abs/html/dblparens.html .

Radu Rădeanu
quelle
32
oder ((++var))oder ((var=var+1))oder ((var+=1)).
gniourf_gniourf
6
Erstaunlicherweise wird var=0; ((var++))ein Fehlercode zurückgegeben, während var=0; ((var++)); ((var++))dies nicht der Fall ist. Irgendeine Idee warum?
Phunehehe
15
@phunehehe Schau mal help '(('. In der letzten Zeile steht:Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise.
Radu Rădeanu
2
Ich vermute die Bewertung von Null, da 1@ gniourf_gniourfs Tipp dies beinhaltet, ((++var))aber nicht ((var++)).
DreadPirateShawn
4
Ist die Verwendung let var++ohne Anführungszeichen sicher ?
wjandrea
162
var=$((var + 1))

Arithmetik in Bash verwendet $((...))Syntax.

Paul Tanzini
quelle
9
Deutlich besser als die akzeptierte Antwort. Auf nur 10% so viel Platz haben Sie es geschafft, genügend Beispiele bereitzustellen (eins ist genug - neun ist übertrieben bis zu dem Punkt, an dem Sie nur angeben), und Sie haben uns genügend Informationen zur Verfügung gestellt, um zu wissen, dass dies ((...))der Schlüssel zur Verwendung von Arithmetik ist in bash. Mir war nicht klar, dass ich nur die akzeptierte Antwort betrachtete - ich dachte, es gäbe ein seltsames Regelwerk über die Reihenfolge der Operationen oder etwas, das zu allen Klammern in der akzeptierten Antwort führte.
ArtOfWarfare
83

Performance-Analyse verschiedener Optionen

Dank der Antwort von Radu Rădeanu , die folgende Möglichkeiten bietet, eine Variable in bash zu erhöhen :

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

Es gibt auch andere Möglichkeiten. Schauen Sie sich zum Beispiel die anderen Antworten auf diese Frage an.

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

So viele Möglichkeiten zu haben, führt zu diesen beiden Fragen:

  1. Gibt es einen Leistungsunterschied zwischen ihnen?
  2. Wenn ja welche, welche schneidet am besten ab?

Inkrementeller Leistungstestcode:

#!/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

Ergebnisse:

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

Fazit:

Es sieht so aus, i+=1als ob Bash bei der Ausführung am schnellsten ist, wenn $ies als Ganzzahl deklariert wird. letAussagen scheinen besonders langsam zu sein und exprsind bei weitem die langsamsten, weil sie nicht eingebaut sind.

wjandrea
quelle
Anscheinend korreliert die Geschwindigkeit mit der Befehlslänge. Ich frage mich, ob die Befehle die gleichen Funktionen aufrufen.
MatthewRock
18

Es gibt auch Folgendes:

var=`expr $var + 1`

Achten Sie auf die Leerzeichen und " ist nicht "

Radus Antworten und Kommentare sind zwar erschöpfend und sehr hilfreich, aber bashspezifisch. Ich weiß, dass Sie speziell nach bash gefragt haben, aber ich dachte, ich würde nachschauen, da ich diese Frage fand, als ich das gleiche mit sh in busybox unter uCLinux machen wollte. Dies ist tragbar jenseits von Bash.

Tphelican
quelle
1
Sie können auchi=$((i+1))
wjandrea
Wenn $(...)in dieser Shell eine Prozessersetzung verfügbar ist, würde ich empfehlen, diese zu verwenden.
Radon Rosborough
7

In allen Antworten fehlt eine Methode - bc

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

bcwird vom POSIX- Standard festgelegt und sollte daher auf allen Versionen von Ubuntu- und POSIX-kompatiblen Systemen vorhanden sein. Die <<<Umleitung kann aus Gründen der echo "$VAR" | bcPortabilität in geändert werden. Da jedoch die Frage lautet, ist bashes in Ordnung, sie nur zu verwenden <<<.

Sergiy Kolodyazhnyy
quelle
6

Der Rückgabecode 1Problem vorhanden ist für alle Standardvarianten ( let, (())usw.). Dies führt häufig zu Problemen, z. B. bei Skripten, die verwendet werden set -o errexit. Hier ist, was ich verwende, um Fehlercode 1aus mathematischen Ausdrücken zu verhindern , die ausgewertet werden 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
quelle
0

Dies muss der schlechteste Weg sein, um eine so einfache Aufgabe zu erledigen, aber ich wollte es nur zum Spaß dokumentieren (komplettes Gegenteil von Codegolf).

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

oder

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

Nehmen Sie ernsthaft eine der anderen viel besseren Möglichkeiten in Anspruch.

Leetbacoon
quelle