Trabalhando com números e formatação
Nesta aula, você aprenderá a trabalhar com números e formatação em Shell Script, dominando o comando printf para saídas formatadas, técnicas de formatação numérica, manipulação de datas com o comando date e realização de cálculos aritméticos com expressões e o comando bc. Ao final, você terá uma base sólida para criar scripts mais robustos e legíveis.
Nesta aula, vamos explorar ferramentas essenciais para lidar com números e formatação em scripts Bash. Você já deve ter usado echo para imprimir mensagens, mas quando precisamos de controle fino sobre a formatação (como alinhar colunas, controlar casas decimais ou exibir datas em formatos específicos), precisamos de ferramentas mais poderosas. Vamos estudar o comando printf, que é inspirado na função homônima da linguagem C, e também veremos como formatar números, trabalhar com datas e realizar cálculos de forma eficiente.
Dominar esses recursos é fundamental para criar scripts profissionais, gerar relatórios, processar dados e automatizar tarefas do dia a dia. Ao final desta aula, você será capaz de produzir saídas claras e bem estruturadas, além de realizar operações matemáticas com precisão.
printf
O comando printf é uma ferramenta versátil para imprimir texto formatado. Diferente do echo, ele não adiciona automaticamente uma nova linha no final, o que nos dá controle total sobre a saída. Sua sintaxe básica é:
printf FORMATO [ARGUMENTOS]
O FORMATO é uma string que pode conter caracteres comuns e especificadores de formato, que começam com %. Cada especificador é substituído pelo argumento correspondente. Por exemplo, %s para strings, %d para inteiros e %f para números de ponto flutuante.
Vamos ver alguns exemplos práticos:
#!/bin/bash
# Exemplo básico
nome="Maria"
idade=30
printf "Nome: %s, Idade: %d anos\n" "$nome" "$idade"
# Com quebra de linha explícita
printf "Olá, mundo!\n"
No primeiro exemplo, %s é substituído pelo valor de nome e %d pelo valor de idade. O \n no final insere uma nova linha. Note que, se não colocarmos \n, a saída não quebrará a linha automaticamente.
Além dos especificadores básicos, podemos usar modificadores para controlar largura, alinhamento e precisão. Por exemplo, %-10s alinha a string à esquerda em um campo de 10 caracteres, enquanto %10s alinha à direita. Para números, %5.2f imprime um número com largura total de 5 caracteres e 2 casas decimais.
#!/bin/bash
# Alinhamento e precisão
printf "%-10s|%10s\n" "Nome" "Idade"
printf "%-10s|%10d\n" "Ana" 25
printf "%-10s|%10d\n" "João" 30
# Números com casas decimais
pi=3.14159
printf "Pi com 2 casas: %.2f\n" "$pi"
printf "Pi com 4 casas: %.4f\n" "$pi"
Esses recursos são extremamente úteis para gerar tabelas alinhadas em relatórios ou logs.
Formatação numérica
Em Bash, números são tratados como strings, mas podemos formatá-los de várias maneiras para exibição. O comando printf é a principal ferramenta, mas também podemos usar sed, awk ou até mesmo bc para manipulações mais complexas.
Uma tarefa comum é adicionar separadores de milhar ou arredondar números. Para separadores de milhar, podemos usar printf com o modificador ' (apóstrofo) em algumas versões do Bash, ou usar sed para inserir vírgulas. Por exemplo:
#!/bin/bash
numero=1234567
# Usando printf com apóstrofo (funciona no Linux)
printf "%'d\n" "$numero" # Saída: 1,234,567
# Alternativa com sed (para compatibilidade)
echo "$numero" | sed -E ':a;s/\b[0-9]+([0-9]{3})/\1,/g;ta' # Saída: 1,234,567
Para arredondamento, podemos usar printf com %.0f para arredondar para o inteiro mais próximo, ou %.2f para duas casas decimais. No entanto, printf usa arredondamento bancário (metade para o par) em alguns casos, o que pode ser surpreendente. Para um arredondamento matemático padrão, podemos usar awk ou bc.
#!/bin/bash
num=2.675
# Arredondamento com printf
printf "%.2f\n" "$num" # Pode imprimir 2.67 ou 2.68 dependendo do sistema
# Usando awk para arredondar para cima
awk -v n="$num" 'BEGIN{printf "%.2f\n", n}'
# Usando bc para arredondar
# bc não tem arredondamento direto, mas podemos usar scale e adicionar 0.5 (para positivos)
echo "scale=2; $num + 0.005" | bc # Adiciona 0.005 e trunca
Outra formatação comum é converter números para diferentes bases, como hexadecimal ou octal. O printf pode fazer isso com %x e %o:
#!/bin/bash
dec=255
printf "Hex: %x\n" "$dec" # Saída: ff
printf "Octal: %o\n" "$dec" # Saída: 377
printf "Hex maiúsculo: %X\n" "$dec" # Saída: FF
Essas técnicas são essenciais para scripts que lidam com dados binários, endereços de memória ou cores em códigos HTML.
Datas (date)
O comando date é a ferramenta padrão para obter e formatar datas e horas no Linux. Ele pode exibir a data atual, ou uma data específica, em vários formatos. A sintaxe básica é:
date [OPÇÕES] [+FORMATO]
O formato é uma string que contém sequências de escape iniciadas por %. Por exemplo, %Y para o ano com 4 dígitos, %m para o mês, %d para o dia, %H para a hora (00-23), %M para minutos e %S para segundos.
#!/bin/bash
# Data atual no formato ISO
hoje=$(date +%Y-%m-%d)
echo "Hoje é $hoje"
# Data e hora completa
echo "$(date '+%d/%m/%Y %H:%M:%S')"
# Usando date em scripts para logs
log_file="log-$(date +%Y%m%d).txt"
echo "Gravando log em $log_file"
Além de exibir a data atual, podemos usar date para calcular datas futuras ou passadas com a opção -d. Por exemplo, para obter a data de amanhã ou a data de 10 dias atrás:
#!/bin/bash
# Data de amanhã
amanha=$(date -d "tomorrow" +%Y-%m-%d)
echo "Amanhã: $amanha"
# Data de 10 dias atrás
dez_dias_atras=$(date -d "10 days ago" +%Y-%m-%d)
echo "10 dias atrás: $dez_dias_atras"
# Adicionar 2 meses
date -d "+2 months" +%Y-%m-%d
Também podemos converter uma string de data para um formato específico, usando -d com uma string e depois o formato de saída. Isso é útil para normalizar datas em scripts.
#!/bin/bash
# Converter uma data em outro formato
data_original="2024-01-15"
data_formatada=$(date -d "$data_original" +"%d/%m/%Y")
echo "Data formatada: $data_formatada" # Saída: 15/01/2024
Para obter o timestamp Unix (segundos desde 1970-01-01), usamos %s. Isso é útil para cálculos de tempo ou para medir intervalos.
#!/bin/bash
# Timestamp atual
agora=$(date +%s)
echo "Timestamp: $agora"
# Medir tempo de execução
inicio=$(date +%s)
# ... algum comando ...
fim=$(date +%s)
duracao=$((fim - inicio))
echo "Tempo de execução: $duracao segundos"
Cálculos
No Bash, podemos realizar cálculos inteiros usando a expansão aritmética $((...)) ou o comando let. Essas operações funcionam apenas com números inteiros. Por exemplo:
#!/bin/bash
# Soma e subtração
soma=$((10 + 5))
subtracao=$((10 - 5))
echo "Soma: $soma, Subtração: $subtracao"
# Multiplicação e divisão
mult=$((10 * 5))
div=$((10 / 3)) # Divisão inteira, resultado 3
echo "Multiplicação: $mult, Divisão: $div"
# Módulo (resto)
resto=$((10 % 3))
echo "Resto: $resto"
# Incremento e decremento
contador=0
((contador++))
echo "Contador: $contador"
Para cálculos com números de ponto flutuante, usamos o comando bc (calculadora de precisão arbitrária). O bc pode realizar operações com decimais e também possui funções matemáticas como raiz quadrada, seno, etc. Para usá-lo, passamos a expressão como uma string e redirecionamos para o bc.
#!/bin/bash
# Soma de decimais
resultado=$(echo "scale=2; 3.14 + 2.5" | bc)
echo "Resultado: $resultado" # Saída: 5.64
# Multiplicação e divisão com precisão
resultado=$(echo "scale=4; 10 / 3" | bc)
echo "10/3 = $resultado" # Saída: 3.3333
# Potência
resultado=$(echo "2^10" | bc)
echo "2^10 = $resultado" # Saída: 1024
# Raiz quadrada (requer -l para math library)
resultado=$(echo "scale=3; sqrt(16)" | bc -l)
echo "sqrt(16) = $resultado" # Saída: 4.000
O bc é muito poderoso e pode ser usado em scripts para cálculos financeiros, científicicos ou qualquer operação que precise de precisão decimal.
Além disso, podemos usar o comando awk para cálculos, pois ele possui suporte nativo a ponto flutuante. Por exemplo:
#!/bin/bash
# Cálculo com awk
resultado=$(awk 'BEGIN{printf "%.2f", 10/3}')
echo "10/3 com awk: $resultado"
Boas práticas e observações finais
Ao trabalhar com números e formatação em scripts, lembre-se de:
- Sempre cite as variáveis entre aspas duplas ao usar
printfpara evitar problemas com espaços ou caracteres especiais. - Prefira
printfem vez deechoquando precisar de formatação precisa ou quando o script precisar ser portável entre sistemas. - Use
bcouawkpara cálculos com ponto flutuante, pois o Bash nativo não suporta. - Teste o arredondamento em seu sistema, pois o comportamento pode variar.
- Documente seus scripts com comentários explicando as fórmulas ou formatos usados.
Com essas ferramentas, você pode criar scripts mais robustos e legíveis, capazes de gerar relatórios formatados, processar dados numéricos e lidar com datas de forma eficiente.
Referências
- Documentação oficial do Bash (Builtins)
- man printf (Linux)
- man date (Linux)
- Documentação do GNU bc
- Documentação do GNU awk
- ShellCheck - analisador de scripts
- Advanced Bash-Scripting Guide
Exercícios
- Escreva um script que use
printfpara exibir uma tabela com 3 colunas: Nome, Idade e Cidade. Alinhe as colunas à esquerda com larguras de 10, 5 e 15 caracteres, respectivamente. Preencha com pelo menos 3 linhas de dados fictícios.✓ Resposta:#!/bin/bash printf "%-10s|%-5s|%-15s\n" "Nome" "Idade" "Cidade" printf "%-10s|%-5d|%-15s\n" "Ana" 25 "São Paulo" printf "%-10s|%-5d|%-15s\n" "Bruno" 32 "Rio de Janeiro" printf "%-10s|%-5d|%-15s\n" "Carla" 28 "Belo Horizonte" - Crie um script que leia um número do usuário e o exiba formatado com separadores de milhar (vírgula) e com duas casas decimais. Use
printfe, se possível, a opção'no modificador.✓ Resposta:#!/bin/bash read -p "Digite um número: " num # Usando printf com apóstrofo (funciona no Linux) printf "%'.2f\n" "$num" # Alternativa: usando awk para formatar com vírgula e 2 decimais awk -v n="$num" 'BEGIN{printf "%\047.2f\n", n}' - Escreva um script que calcule a idade de uma pessoa com base na data de nascimento fornecida no formato AAAA-MM-DD. Use o comando
datepara obter a data atual e calcular a diferença em anos. Dica: use o timestamp Unix.✓ Resposta:#!/bin/bash read -p "Digite sua data de nascimento (AAAA-MM-DD): " nasc nasc_ts=$(date -d "$nasc" +%s) hoje_ts=$(date +%s) diff=$(( (hoje_ts - nasc_ts) / (365*24*3600) )) echo "Sua idade é aproximadamente $diff anos." - Use
bcpara calcular a média de três números fornecidos pelo usuário, com precisão de 2 casas decimais. O script deve pedir os três números e exibir o resultado.✓ Resposta:#!/bin/bash read -p "Digite o primeiro número: " n1 read -p "Digite o segundo número: " n2 read -p "Digite o terceiro número: " n3 media=$(echo "scale=2; ($n1 + $n2 + $n3) / 3" | bc) echo "A média é $media" - Crie um script que exiba a data atual em vários formatos, incluindo: AAAA-MM-DD, DD/MM/AAAA, com hora completa, e o timestamp Unix. Use
dateeprintfpara alinhar as saídas.✓ Resposta:#!/bin/bash printf "%-20s %s\n" "ISO (AAAA-MM-DD):" "$(date +%Y-%m-%d)" printf "%-20s %s\n" "Brasileiro (DD/MM/AAAA):" "$(date +%d/%m/%Y)" printf "%-20s %s\n" "Completo:" "$(date '+%d/%m/%Y %H:%M:%S')" printf "%-20s %s\n" "Timestamp Unix:" "$(date +%s)"