Questão Como incrementar uma variável no bash?


Eu tentei incrementar uma variável numérica usando var=$var+1 e var=($var+1) sem sucesso. A variável é um número, embora bash pareça estar lendo como uma string.

Bash versão 4.2.45 (1) -release (x86_64-pc-linux-gnu) no Ubuntu 13.10.


455
2017-12-03 16:34


origem




Respostas:


Há mais de uma maneira de incrementar uma variável no bash, mas o que você tentou não está correto.

Você pode usar por exemplo expansão aritmética:

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

Ou você pode usar let:

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

Veja também: http://tldp.org/LDP/abs/html/dblparens.html.


722
2017-12-03 16:39



ou ((++var)) ou ((var=var+1)) ou ((var+=1)). - gniourf_gniourf
ou var = $ (expr $ var + 1) - Javier López
Curiosamente, var=0; ((var++)) retorna um código de erro enquanto var=0; ((var++)); ((var++)) não. Alguma ideia do porquê? - phunehehe
@phunehehe Olhe para help '(('. A última linha diz: Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise. - Radu Rădeanu
é seguro usar let var++, sem as aspas? - wjandrea


var=$((var + 1))

Aritmética nos usos do bash $((...)) sintaxe.


101
2017-12-03 16:38



Muito melhor que a resposta aceita. Em apenas 10% do espaço, você conseguiu fornecer exemplos suficientes (um é muito - nove é um exagero quando você está apenas se exibindo), e você nos forneceu informações suficientes para saber que ((...))é a chave para usar a aritmética no bash. Eu não percebi que apenas olhando para a resposta aceita - eu pensei que havia um estranho conjunto de regras sobre a ordem de operações ou algo levando a todos os parênteses na resposta aceita. - ArtOfWarfare


Análise de desempenho de várias opções

Graças a A resposta de Radu Rădeanu que fornece as seguintes maneiras de incrementar uma variável no bash:

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

Existem outras maneiras também. Por exemplo, procure nas outras respostas sobre essa questão.

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

Ter tantas opções leva a estas duas questões:

  1. Existe uma diferença de desempenho entre eles?
  2. Se sim, qual o melhor desempenho?

Código de teste de desempenho incremental:

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

Resultados:

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

Conclusão:

Parece que o bash é o mais rápido no desempenho i+=1 quando $i é declarado como um inteiro. let declarações parecem particularmente lentas, e expr é de longe o mais lento porque não é um construído.


62
2017-07-31 17:15



Aparentemente, a velocidade se correlaciona com o comprimento do comando. Eu me pergunto se os comandos chamam as mesmas funções. - MatthewRock
i=(expr ...) é um erro de sintaxe. Você quis dizer i=$(expr ...)? - muru
@muru corrigiu e adicionou um cheque no loop for. - wjandrea


Tem também isso:

var=`expr $var + 1`

Tome nota cuidadosa dos espaços e também ` não é '

Embora as respostas de Radu e os comentários sejam exaustivos e muito úteis, eles são específicos do bash. Eu sei que você perguntou especificamente sobre o bash, mas eu pensei em fazer um pipe desde que eu encontrei essa pergunta, quando eu estava olhando para fazer a mesma coisa usando sh in busybox sob uCLinux. Este portátil além do bash.


14
2017-08-22 23:11



Você também pode usar i=$((i+1)) - wjandrea
Se substituição de processo $(...) está disponível neste shell, eu recomendo usá-lo em seu lugar. - Radon Rosborough


Se você declarar $var como um inteiro, então o que você tentou na primeira vez irá realmente funcionar:

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

Referência: Tipos de variáveis, Guia do Bash para Iniciantes


9
2017-12-06 22:19





Há um método faltando em todas as respostas - bc

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

bc é especificado por POSIX padrão, por isso deve estar presente em todas as versões do Ubuntu e sistemas compatíveis com POSIX. o <<< redirecionamento pode ser alterado para echo "$VAR" | bc portabilidade, mas como a pergunta é sobre bash - não há problema em usar <<<.


6
2018-02-23 13:58





O código de retorno 1 problema está presente para todas as variantes padrão (let, (()), etc.). Isso costuma causar problemas, por exemplo, em scripts que usam set -o errexit. Aqui está o que estou usando para evitar o código de erro 1 de expressões matemáticas que avaliam a 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