Domanda Come incrementare una variabile in bash?


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

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


455
2017-12-03 16:34


origine




risposte:


C'è più di un modo per incrementare una variabile in bash, ma quello che hai provato non è corretto.

Puoi usare per esempio espansione aritmetica:

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

O puoi usare let:

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

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


720
2017-12-03 16:39



o ((++var)) o ((var=var+1)) o ((var+=1)). - gniourf_gniourf
o var = $ (expr $ var + 1) - Javier López
Curiosamente, var=0; ((var++)) restituisce un codice di errore mentre var=0; ((var++)); ((var++)) non. Qualche idea del perché? - phunehehe
@phunehehe Guarda help '(('. L'ultima riga dice: Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise. - Radu Rădeanu
è sicuro da usare let var++senza le virgolette? - wjandrea


var=$((var + 1))

Aritmetica negli usi di bash $((...)) sintassi.


101
2017-12-03 16:38



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


Analisi delle prestazioni di varie opzioni

Grazie a La 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. Se sì quale, che si comporta meglio?

Codice del test delle prestazioni incrementali:

#!/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 il più veloce a esibirsi i+=1 quando $i è dichiarato come numero intero. let le dichiarazioni sembrano particolarmente lente, e expr è di gran lunga il più lento perché non è un built-in.


62
2017-07-31 17:15



Apparentemente la velocità è correlata alla lunghezza del comando. Mi chiedo se i comandi chiamino le stesse funzioni. - MatthewRock
i=(expr ...) è un errore di sintassi. Intendevi i=$(expr ...)? - muru
@muru risolto e aggiunto un controllo nel ciclo for. - wjandrea


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 bugia. So che hai fatto specificamente domande su bash, ma ho pensato che avrei fatto pipì da quando ho trovato questa domanda quando stavo cercando di fare la stessa cosa usando sh in busybox sotto uCLinux. Questo portatile oltre bash.


14
2017-08-22 23:11



Puoi anche usare i=$((i+1)) - wjandrea
Se la sostituzione del processo $(...) è disponibile su questa shell, ti consiglio di usarlo invece. - Radon Rosborough


Se dichiari $var come un intero, quindi quello che hai provato la prima volta funzionerà davvero:

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

Riferimento: Tipi di variabili, Bash Guide for Beginners


9
2017-12-06 22:19





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 da POSIX standard, quindi dovrebbe essere presente su tutte le versioni di sistemi compatibili con Ubuntu e POSIX. Il <<< il reindirizzamento potrebbe essere modificato echo "$VAR" | bc per la portabilità, ma dal momento che la domanda chiede informazioni bash - Va bene solo da usare <<<.


6
2018-02-23 13:58





Il codice di ritorno 1 problema è presente per tutte le varianti di default (let, (()), eccetera.). Ciò spesso causa problemi, ad es. Negli script che usano set -o errexit. Ecco cosa sto usando per prevenire il codice di errore 1 dalle 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

4