Percursos
/
Bash
Bash
/
Programa
/
Aritmética
Ar

Aritmética em Bash

{one: "1 exercício", many: "%{count} exercícios", other: "%{count} exercícios"}

Sobre Aritmética

O Bash consegue fazer aritmética, mas apenas com números inteiros. Se precisares de fazer cálculos com números de vírgula flutuante, tens de recorrer a um programa externo (como o awk ou o bc).

Os operadores aritméticos estão listados (por ordem de precedência) no manual: Shell Arithmetic.

Além disso, o Bash só realiza aritmética em certos sítios (conhecidos como "contextos" aritméticos).

Expansão aritmética

A expansão aritmética é parecida com a substituição de comandos, mas com parênteses duplos.

$(( expression ))

Esta sintaxe expande-se para o resultado da avaliação da expressão. É habitualmente usada para guardar o resultado numa variável:

x=$(( 3 + 4 ))
Note

A quantidade de espaços em branco dentro dos parênteses é ao teu critério. É uma questão de estilo e de legibilidade.

x=$(( 3 + 4 ))
y=$((5+6))

O teu simpático autor gosta de acrescentar espaços para que a expressão aritmética fique mais visível dentro dos parênteses.

Condicional aritmética

O condicional aritmético é executado dentro de parênteses duplos sem o $ inicial:

(( expression ))

Isto não se expande para o resultado. No entanto, produz um estado de saída.

  • O estado de saída é 0 (sucesso) se o resultado da expressão for diferente de zero.
  • O estado de saída é 1 (falha) se o resultado da expressão for zero.
Note

Dentro de ((, o Bash usa o mesmo sentido de verdadeiro/falso que a linguagem C: zero é falso, diferente de zero é verdadeiro. Isto é o oposto da forma como o Bash avalia os estados de saída!

Esta sintaxe é normalmente usada em dois sítios:

  1. como a parte COMMAND de uma condicional if ou de um ciclo while.

    if (( x == y - 1 )); then ...
    while (( y > 0 )); do ...
    
  2. como um comando isolado para modificar o valor de uma variável.

    (( x = x + 1 ))
    echo "new value for x is $x"
    
    Caution

    Tem cuidado ao usar aritmética desta forma em conjunto com set -e. Se a expressão for avaliada como zero, o estado de saída será diferente de zero e o Bash termina.

    x=0
    (( x++ ))
    echo "you will see this"
    
    set -e
    x=0
    (( x++ ))
    echo "shell exits on the previous line; you won't see this"
    

    Para uma discussão mais detalhada sobre o operador ++, consulta este artigo sobre pós-incremento vs. pré-incremento

Outros sítios com contexto aritmético

Há mais alguns sítios que são contextos aritméticos:

  • O ciclo for aritmético.

    for (( i = 0; i < 10; i++ )); do
        echo "$i"
    done
    
  • A parte do índice de um array com índice numérico.

    echo "The next element is ${array[x + 1]}"
    # ................................^^^^^
    

    Para saberes mais sobre arrays, lê o documento de conceito Arrays.

  • As partes do deslocamento e do comprimento da expansão ${var:offset:length}.

  • O comando interno let.

    • Repara que (( expression )) é preferível a let "expresson". Ao contrário da construção condicional aritmética, a expressão let está sujeita a expansão de nomes de ficheiros e a divisão de palavras, e exige o uso de aspas.
  • Atribuição de variáveis, quando a variável tem o "atributo inteiro".

    # variable x does not have the integer attribute
    x="3 + 4"
    declare -p x      # => declare -- x="3 + 4"
    
    # variable y has the integer attribute
    declare -i y
    y="4 + 5"
    declare -p y      # => declare -i y="9"
    
    Note

    Na opinião do autor, o declare -i tem uma utilidade limitada. O seu principal benefício é permitir aritmética "simples" (sem a sintaxe de expressão aritmética). Isto pode causar confusão a quem lê o teu código, porque as expressões aritméticas são avaliadas em sítios inesperados.

Usar variáveis em expressões aritméticas

Há um aspeto dos contextos aritméticos que melhora a legibilidade.

height=10
width=20
area=$(( height * width ))

Reparaste que os sinais de dólar $ desapareceram das variáveis na expressão aritmética? O Bash permite-te fazer isso. Torna as expressões muito mais fáceis de ler.

O manual diz:

Dentro de uma expressão, as variáveis da shell também podem ser referidas pelo nome sem usar a sintaxe de expansão de parâmetros.

Funciona até para elementos de arrays:

point1=(10 15)  # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))

Mas outras expressões de parâmetros têm de usar a sua sintaxe especial. Por exemplo, duplicar o comprimento de uma string:

s="hello world"
len=$(( ${#s} * 2 ))

E os parâmetros posicionais também. Se os dois primeiros parâmetros posicionais representarem a largura e a altura:

area=$(( $1 * $2 ))
Editar via GitHub A ligação abre numa nova janela ou separador