Aula 8 – Operações Aritméticas

Aula 7 – Concatenação de Strings | Índice | Aula 9 – Expansões do Shell


Seu apoio é muito importante para a criação e a manutenção dos cursos gratuitos do canal debxp:


8.1 – As operações básicas

O Bash é capaz de realizar as operações aritméticas básicas de quatro formas, todas elas limitadas a números inteiros:

  • Comando interno: let expressão
  • Comando composto: (( expressão ))
  • Expansão aritmética: $(( expressão ))
  • Com valores definidos como inteiros: declare -i nome

Além disso, no interior dos colchetes ([...]) do índice de um vetor indexado, os valores são tratados como inteiros pelo Bash, o que nos permite realizar operações aritméticas com eles. Isso, por exemplo, seria perfeitamente válido:

vetor[n++]

Todos esses métodos são capazes de lidar com as operações aritméticas básicas, mas funcionam de formas muito diferentes, como veremos adiante. Antes, porém, nós precisamos conhecer os símbolos que, independente do método, irão representar as operações aritméticas que veremos neste curso.

8.2 – Operadores aritméticos

Operador Descrição
+ Soma
- Subtração
* Multiplicação
/ Divisão
% Módulo (resto)
** Potenciação (exponenciação)

8.3 – Operadores de atribuição

Operador Descrição
nome=valor Atribui um valor a "nome"
nome+=valor Soma um valor ao valor atual em "nome"
nome-=valor Subtrai um valor do valor atual em "nome"
nome*=valor Multiplica o valor atual em "nome" por outro valor
nome/=valor Divide o valor atual em "nome" por outro valor
nome%=valor Substitui o valor atual em "nome" pelo resto da divisão por outro valor
nome++ Pós-incremento: retorna o valor atual em "nome" e soma 1
nome-- Pós-decremento: retorna o valor atual em "nome" e subtrai 1
++nome Pré-incremento: atribui a "nome" o seu valor atual mais 1
--nome Pré-decremento: atribui a "nome" o seu valor atual menos 1

Exemplos:

numero=10    # Atribui o valor 10 à variável ‘numero’ (10) 
numero+=3    # Soma 3 ao valor já definido em ‘numero’ (13)
numero*=2    # Multiplica o valor em ‘numero’ por 2 (26)

O que seria o mesmo que…

numero=10          # Atribui o valor 10 à variável ‘numero’ (10) 
numero=numero+3    # Soma 3 ao valor já definido em ‘numero’ (13)
numero=numero*2    # Multiplica o valor em ‘numero’ por 2 (26)

Os operadores de pré e pós incremento e de pré e pós decremento, por sua vez, sempre efetuam a soma ou a subtração de 1 sobre o valor atual em nome. A diferença entre o "pré" e o "pós" diz respeito ao momento em que o novo valor estará disponível. Veja no exemplo:

:~$ nota=8
:~$ echo $((nota++)) # pós-incremento
8
:~$ echo $nota
9

Repare que, no momento que a expansão aritmética aconteceu (quando executamos o echo), o valor em nota ainda era o valor anterior (8). Mas, logo em seguida, a operação foi efeutada e o valor passou a ser 9.

No caso do pré incremento ou decremento, a operação é efetuada antes da expansão acontecer:

:~$ nota=8
:~$ echo $((++nota))
9
:~$ echo $nota
9

8.4 – Precedência

Quanto à ordem de precedência (quem é executado primeiro),os operadores aritméticos seguem as mesmas regras da matemática:

  • As operações são efetuadas da esqueda para a direita;
  • Primeiro efetua-se o que está entre parêntesis;
  • Depois as exponenciações;
  • Depois as multiplicações, divisões e módulos;
  • Por último, as somas e subtrações.

8.5 – O problema do ‘declare -i’

Toda variável no Bash é criada como sendo de tipo indefinido e seu valor é tratado como uma string. Além disso, com exceção das arrays associativas, nós não somos obrigados a declarar explicitamente o tipo de valor que será armazenado numa variável. Porém, quando trabalhamos com valores numéricos, em algumas situações pode ser interessante explicitar que o valor é um inteiro. Então, nós utilizamos o comando builtin declare:

declare -i nome[=valor] # Onde “=valor” é opcional.

Isso ativa o atributo de inteiro da variável, fazendo com que ela seja tratada como um valor numérico e, portanto, capaz de participar de algumas operações aritméticas.

Por exemplo, sem o atributo de inteiro ligado…

:~$ nota=6
:~$ nota=$nota+2
:~$ echo $nota
6+2

Mas, ativando o atributo de inteiro da variável nota

:~$ declare -i nota
:~$ nota=6
:~$ nota=$nota+2
:~$ echo $nota
8

Nós também poderíamos reatribuir o valor em ”nota” incrementando seu valor:

:~$ declare -i nota
:~$ nota=6
:~$ nota+=2
:~$ echo $nota
8

Mas, os problemas começam quando tentamos utilizar outros operadores de (re)atribuição, por exemplo:

:~$ declare -i nota
:~$ nota=6
:~$ nota=$nota-2
:~$ echo $nota
4
:~$ nota-=2
bash: nota-=2: comando não encontrado

Ou seja, o único operador de (re)atribuição compacto que podemos utilizar é o +=, porque somente ele existe (do ponto de vista do Bash) como um operador válido para qualquer tipo de valor na linha do comando — se o valor for uma string, acontecerá uma concatenação, se for um inteiro, haverá um incremento.

Essa inconsistência (em relação às outras formas de realizar operações aritméticas) já é um sinal de que declarar o valor como inteiro não é algo trivial e merece uma atenção toda especial.

Outro problema do uso indiscriminado do declare -i com a única finalidade de permitir cálculos aritméticos nos scripts, é que isso afeta a legibilidade do código e, portanto, dificulta seu entendimento e a correção de erros. O principal problema está na presunção de um comportamento padrão, por exemplo:

:~$ nota=2+2
:~$ echo $nota
2+2

Isso é o esperado quando nós lemos a linha do comando acima, ou seja, o valor em nota é a string 2+2, a menos que, em algum lugar, ou em algum momento, o valor da variável tenha sido declarado como um inteiro…

declare -i nota

...um monte de código...

nota=2+2
echo $nota    # resultaria em '4'...

Essas inconsistências devem ser sempre evitadas nos nossos scripts e, além disso, o Bash tem formas muito mais interessantes de efetuar cálculos sem que os atributos de uma variável sejam alterados.

8.6 – O comando interno ‘let’

O comando interno let (no sentido de “tornar”, em inglês), é utilizado para criar variáveis numéricas e realizar operações aritméticas com elas. O que ele faz é tratar strings numéricas que representem valores inteiros como inteiros literais sem alterar os atributos das variáveis.

Por exemplo:

:~$ let nota=2
:~$ echo $nota
2
:~$ nota=$nota+5
:~$ echo $nota
2+5    # O valor em 'nota' continua sendo tratado como string...

O mais importante aqui é você perceber que os nomes, valores e operadores utilizados com o let são seus parâmetros, e isso significa que a interpretação das expressões é feita internamente pelo próprio comando, não pelo shell.

Observe:

:~$ let nota=2
:~$ echo $nota
2
:~$ let nota=nota+2
:~$ echo $nota
4

Reparou que, no let, nós não precisamos utilizar o $ para expandir o valor em nota? Isso só é possível porque não é o shell que está interpretando a expressão. Mas, isso também é válido:

:~$ let nota=2
:~$ echo $nota
2
:~$ let nota=$nota+2
:~$ echo $nota
4

Porque, independente do comando que recebe os parâmetros, o shell sempre realizará expansões antes de executar a linha do comando. Quer dizer, quando o let foi efetivamente executado, a expressão que ele recebeu como parâmetro era…

let nota=2+2

Efetuando múltiplas expressões

Você também pode passar várias expressões de uma vez para o let:

:~$ let nota=2 nota=nota+2 nota-=3
:~$ echo $nota
1

Cada uma dessas três expressões é um parâmetro do comando let.

Outra característica do comando let, é que ele permite a construção de expressões com espaços. Mas, como os espaços são os separadores dos parâmetros que passamos para o comando (as expressões, como no exemplo anterior), esse tipo de construção deve ser feita entre aspas.

Por exemplo:

:~$ let 'nota = 5'
:~$ echo $nota
5

Então, para efetuar múltiplas expressões com um mesmo comando, nós temos duas alternativas:

Alternativa 1: cada expressão que contenha espaços deverá vir entre aspas…

:~$ let 'nota = 5' ++nota
:~$ echo $nota
6

Alternativa 2: tudo entre aspas, mas as expressões devem ser separada por uma vírgula…

:~$ let 'nota = 5, ++nota'
:~$ echo $nota
6

8.7 – O comando composto ‘(( expressão ))’

O comando composto ((...)) é a forma “padrão” de fazer o Bash interpretar expressões aritméticas. Seu comportamento é idêntico ao do comando interno let, com algumas poucas diferenças. Por exemplo, diferente do let, múltiplas expressões devem sempre ser separadas por vírgulas:

:~$ ((nota=2, nota=nota+2, nota-=3))
:~$ echo $nota
1

E também não precisamos de aspas para usar espaços nas expressões:

:~$ ((nota = 2, nota = nota + 2, nota -= 3))
:~$ echo $nota
1

8.8 – A expansão aritmética ‘$(( expressão ))’

As expansões do Bash serão assunto da próxima aula, mas nós já tivemos uma prévia de algumas delas em vários momentos até aqui e está na hora de falar de mais uma: a expansão aritmética.

Diferente dos comandos let e ((...)), de quem ela herda todas as características vistas até aqui, a expansão aritmética efetua as expressões e fornece um valor como retorno na saída padrão, o que é muito prático e poderoso.

Veja o exemplo:

:~$ echo $((nota = 2, nota = nota + 2, nota -= 3))
1
:~$ echo $nota
1

8.9 – E os números não inteiros?

Infelizmente, o Bash não dá suporte a operações com números não inteiros. Para isso, nós sempre teremos que recorrer a alguma outra ferramenta, como a calculadora programável bc, ou a linguagem de processamento de dados tabulares awk (minha solução preferida), mas isso foge do escopo deste curso. Porém, como o bc é uma solução muito popular, aqui está uma pequena amostra de como ele funciona.

# O ‘bc’ lê um arquivo ou uma string na entrada padrão
# e efetua a operação. Por padrão, ele retorna a parte
# inteira do resultado...

:~$ bc <<< '7 / 3'
2

# Para ver as casas decimais, nós utilizamos a opção '-l'...

:~$ bc -l <<< "7 / 3"
2.33333333333333333333

# Se quisermos um número fixo de casas decimais, nós temos
# que passar o argumento 'scale' junto com a operação...

:~$ bc <<< "scale=2; 7 / 3"
2.33

O bc não vem instalado por padrão em todas as distribuições GNU/Linux, mas não deve ser difícil descobrir como instalá-lo. No Debian e derivados, o comando para instalação é:

sudo apt install bc

Você encontra mais informações sobre ele, suas opções e seus operadores no manual:

:~$ man bc

Aula 7 – Concatenação de Strings | Índice | Aula 9 – Expansões do Shell

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Post comment