Come incrementare una variabile in bash?


609

Ho provato ad incrementare una variabile numerica usando entrambi var=$var+1e var=($var+1)senza successo. La variabile è un numero, sebbene bash sembra leggerlo come una stringa.

Versione di Bash 4.2.45 (1) -release (x86_64-pc-linux-gnu) su Ubuntu 13.10.

Risposte:


948

Esiste più di un modo per incrementare una variabile in bash, ma ciò che hai provato non è corretto.

È possibile utilizzare ad esempio l'espansione aritmetica :

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

Oppure puoi usare let:

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

Vedi anche: http://tldp.org/LDP/abs/html/dblparens.html .


31
oppure ((++var))oppure ((var=var+1))oppure ((var+=1)).
gniourf_gniourf,

6
Curiosamente, var=0; ((var++))restituisce un codice di errore mentre var=0; ((var++)); ((var++))non lo fa. Qualche idea sul perché?
phunehehe,

15
@phunehehe Guarda help '(('. L'ultima riga dice:Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise.
Radu Rădeanu,

2
Sospetto che la valutazione di zero 1sia la ragione per cui il suggerimento di @ gniourf_gniourf include ((++var))ma non ((var++)).
DreadPirateShawn,

4
è sicuro da usare let var++, senza virgolette?
wjandrea,

161
var=$((var + 1))

L'aritmetica in bash usa la $((...))sintassi.


9
Molto meglio della risposta accettata. In appena il 10% di spazio in più, sei riuscito a fornire abbastanza esempi (uno è sufficiente - nove è eccessivo al punto in cui ti stai solo esibendo) e ci hai fornito informazioni sufficienti per sapere che ((...))è la chiave per usare l'aritmetica in bash. Non mi rendevo conto che solo guardando la risposta accettata - pensavo che ci fosse una strana serie di regole sull'ordine delle operazioni o qualcosa che portava a tutta la parentesi nella risposta accettata.
ArtOfWarfare il

82

Analisi delle prestazioni di varie opzioni

Grazie alla risposta di Radu Rădeanu che fornisce i seguenti modi per incrementare una variabile in bash:

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

Ci sono anche altri modi. Ad esempio, guarda le altre risposte a questa domanda.

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

Avere così tante opzioni porta a queste due domande:

  1. C'è una differenza di prestazioni tra loro?
  2. In tal caso quale, quale si comporta meglio?

Codice test di prestazione incrementale:

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

risultati:

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

Conclusione:

Sembra che bash sia più veloce i+=1quando $iviene dichiarato come intero. letle dichiarazioni sembrano particolarmente lente, ed exprè di gran lunga la più lenta perché non è integrata.


Apparentemente la velocità è correlata alla lunghezza del comando. Mi chiedo se i comandi chiamino le stesse funzioni.
Matthew Rock

18

C'è anche questo:

var=`expr $var + 1`

Prendi nota degli spazi e anche " non è "

Mentre le risposte di Radu e i commenti sono esaustivi e molto utili, sono specifici per la bash. So che hai chiesto specificamente di bash, ma ho pensato di fare pipa da quando ho trovato questa domanda quando stavo cercando di fare la stessa cosa usando sh in busybox sotto uCLinux. Questo portatile oltre bash.


1
Puoi anche usarei=$((i+1))
wjandrea il

Se la sostituzione del processo $(...)è disponibile su questa shell, raccomanderei invece di usarla.
Radon Rosborough,


7

C'è un metodo mancante in tutte le risposte - bc

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

bcè specificato dallo standard POSIX , quindi dovrebbe essere presente su tutte le versioni di Ubuntu e sistemi compatibili con POSIX. Il <<<reindirizzamento potrebbe essere modificato in echo "$VAR" | bcper portabilità, ma poiché la domanda si pone su bash- Va bene usare solo <<<.


6

Il codice di ritorno 1problema è presente per tutte le varianti di default ( let, (()), ecc). Questo spesso causa problemi, ad esempio, negli script che usano set -o errexit. Ecco cosa sto usando per impedire il codice di errore 1dalle espressioni matematiche che valutano 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

0

Questo deve essere il modo peggiore per compiere un compito così semplice, ma immagino volesse semplicemente documentarlo per divertimento (opposto al codice golf).

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

o

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

Usa seriamente una delle altre scelte molto migliori qui.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.