kb.erickguedes.com
Bash e Shell Script: Automação Unix/Linux

Processamento de Texto e Arquivos

Aula 4 de 6

grep — Busca em Texto

Básico

# Busca simples
grep "erro" app.log

# Ignorar maiúsculas/minúsculas
grep -i "warning" app.log

# Linhas que NÃO contêm
grep -v "debug" app.log

# Número da linha
grep -n "error" app.log

# Contar ocorrências
grep -c "timeout" app.log

Recursivo

# Recursivo em diretório
grep -r "TODO" src/

# Apenas nomes de arquivo
grep -rl "function" src/

# Seguir symlinks
grep -R "config" /etc/

# Especificar include/exclude
grep -r --include="*.js" --exclude="*.min.js" "import" src/

Contexto

# Linhas antes (-B), depois (-A) e ao redor (-C)
grep -B 2 "error" log.txt   # 2 linhas antes
grep -A 5 "trace" log.txt   # 5 linhas depois
grep -C 3 "exception" log.txt  # 3 linhas ao redor

Perl Regex

# Regex estendido com -P (GNU grep)
grep -oP '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' access.log

# Lookahead/lookbehind
grep -oP '(?<=user:)\w+' users.txt

sed — Stream Editor

Substitute

# Substituir primeira ocorrência por linha
sed 's/antigo/novo/' arquivo.txt

# Substituir todas (global)
sed 's/antigo/novo/g' arquivo.txt

# Substituir na enésima ocorrência
sed 's/antigo/novo/2' arquivo.txt  # 2ª ocorrência

# Ignorar case
sed 's/antigo/novo/gi'

# In-place (editar o arquivo)
sed -i 's/erro/error/g' arquivo.txt
sed -i.bak 's/foo/bar/g' arquivo.txt  # backup

# Usar delimitador alternativo
sed 's|/caminho/a|/caminho/b|g'
sed 's@antigo@novo@g'

Delete e Insert

# Deletar linhas
sed '3d' arquivo.txt          # linha 3
sed '5,10d' arquivo.txt       # linhas 5-10
sed '/^#/d' arquivo.txt       # linhas começando com #
sed '/^$/d' arquivo.txt       # linhas vazias
sed '/debug/d' arquivo.txt    # linhas com "debug"

# Insert e append
sed '2i\nova linha' arquivo.txt    # antes da linha 2
sed '3a\nova linha' arquivo.txt    # depois da linha 3

Address Ranges

# Entre dois padrões
sed '/BEGIN/,/END/s/foo/bar/g' arquivo.txt

# Da linha 10 até o final
sed '10,$s/foo/bar/g' arquivo.txt

# Negar endereço
sed '/^#/!s/foo/bar/g' arquivo.txt  # tudo exceto comentários

# Múltiplos comandos (-e)
sed -e 's/foo/bar/g' -e '/^$/d' arquivo.txt

# Script sed
sed -f comandos.sed arquivo.txt

Multiline

# Trabalhar com múltiplas linhas
sed '/start/,/end/{
    /start/!{
        /end/!{
            s/foo/bar/g
        }
    }
}' arquivo.txt

awk — Linguagem de Processamento

Pattern / Action

# Estrutura: awk 'padrão { ação }' arquivo

# Imprimir colunas (default separador espaço)
awk '{ print $1, $3 }' arquivo.txt

# Com cabeçalho
awk 'NR==1 { print "COLUNAS: " $0 } NR>1 { print $1 }' dados.csv

# Separador customizado
awk -F: '{ print $1, $6 }' /etc/passwd

# Com BEGIN e END
awk 'BEGIN { print "Início" } { sum += $1 } END { print "Total:", sum }' numeros.txt

Variáveis Especiais

# NR: número do registro (linha)
# NF: número de campos na linha
# $0: linha completa
# $1, $2, ...: campos individuais
# FS: field separator
# OFS: output field separator
# RS: record separator
# ORS: output record separator

awk 'NR>1 && NR<10 { print NR, NF, $0 }' arquivo.txt
awk '{ for (i=1; i<=NF; i++) print $i }' arquivo.txt

Arrays Associativos

# Contagem de ocorrências
awk '{ count[$1]++ } END { for (k in count) print k, count[k] }' dados.txt

# Agrupar por chave
awk -F, '{ dept=$2; salaries[dept] += $3; count[dept]++ }
    END { for (d in salaries) print d, salaries[d]/count[d] }' funcionarios.csv

printf

# Formatação alinhada
awk '{ printf "%-20s %8d\n", $1, $2 }' dados.txt

# Com precisão decimal
awk '{ printf "Nome: %s - Média: %.2f\n", $1, ($2+$3+$4)/3 }' notas.txt

Outras Ferramentas

# cut — extrair colunas
cut -d: -f1,3 /etc/passwd           # delimitador :
cut -c1-10 arquivo.txt               # caracteres 1-10

# sort — ordenar
sort arquivo.txt                     # alfabético
sort -n numeros.txt                  # numérico
sort -r arquivo.txt                  # reverso
sort -t: -k3 -n /etc/passwd         # por campo
sort -u arquivo.txt                  # único

# uniq — remover duplicatas (requer sort primeiro)
sort dados.txt | uniq
sort dados.txt | uniq -c            # contar
sort dados.txt | uniq -d            # apenas duplicatas

# wc — word count
wc -l arquivo.txt                    # linhas
wc -w arquivo.txt                    # palavras
wc -c arquivo.txt                    # bytes

# tr — translate
echo "hello" | tr 'a-z' 'A-Z'       # maiúsculas
echo "a,b,c" | tr ',' '\n'          # substituir delimitador
echo "a  b   c" | tr -s ' '         # squeeze espaços
echo "abc123" | tr -d '0-9'         # deletar dígitos

# diff / patch
diff -u arquivo1.txt arquivo2.txt   # unified diff
diff -r dir1/ dir2/                 # recursivo
diff -q dir1/ dir2/                 # apenas diferentes
diff arquivo1.txt arquivo2.txt > patch.diff
patch < patch.diff                  # aplicar patch

find — Localizar Arquivos

# Básico
find . -name "*.txt"
find . -iname "README.*"           # case insensitive
find . -type f -name "*.md"        # apenas arquivos
find . -type d -name "src"         # apenas diretórios

# Por tempo
find . -mtime -7                   # modificados nos últimos 7 dias
find . -mtime +30                  # modificados há mais de 30 dias
find . -atime -1                   # acessados no último dia
find /tmp -mmin -60                # modificados na última hora

# Por tamanho
find . -size +10M                  # maior que 10MB
find . -size -1k                   # menor que 1KB
find . -empty                      # arquivos vazios

# Executar comandos
find . -name "*.tmp" -exec rm {} \;           # por arquivo
find . -name "*.tmp" -exec rm {} +            # lote
find . -name "*.log" -exec gzip {} \;         # compactar
find . -type d -name "node_modules" -prune    # pular diretório

# -delete (cuidado!)
find . -name "*.bak" -delete

# Permissões
find . -perm 644
find . -perm /u=w              # gravável pelo owner
find . -user maria
find / -nouser                 # órfãos

Lab: Relatório de Logs com Pipeline Completo

cat > relatorio-log.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail

LOG="${1:-/var/log/syslog}"

if [[ ! -f "$LOG" || ! -r "$LOG" ]]; then
    echo "Arquivo de log inválido: $LOG" >&2
    exit 1
fi

echo "=== RELATÓRIO DE LOGS ==="
echo "Arquivo: $LOG"
echo "Data: $(date)"
echo ""

# 1. Estatísticas gerais
echo "=== ESTATÍSTICAS ==="
wc -l "$LOG" | awk '{print "Total de linhas:", $1}'

# 2. Top IPs (grep + awk + sort + uniq)
echo ""
echo "=== TOP 10 IPs ==="
grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' "$LOG" |
    sort |
    uniq -c |
    sort -rn |
    head -10 |
    awk '{printf "  %-15s %d ocorrências\n", $2, $1}'

# 3. Distribuição por horário (grep + sed + awk)
echo ""
echo "=== DISTRIBUIÇÃO POR HORA ==="
awk '{
    match($0, /[0-9]{2}:[0-9]{2}:[0-9]{2}/);
    if (RSTART) {
        hora = substr($0, RSTART, 2);
        horas[hora]++
    }
}
END {
    for (h = 0; h < 24; h++) {
        printf "  %02dh: %d ocorrências\n", h, horas[sprintf("%02d", h)]+0
    }
}' "$LOG"

# 4. Níveis de severidade (grep case insensitive + awk)
echo ""
echo "=== SEVERIDADE ==="
for nivel in ERROR WARN INFO DEBUG; do
    count=$(grep -ci "$nivel" "$LOG" 2>/dev/null || echo 0)
    printf "  %-5s: %d\n" "$nivel" "$count"
done

# 5. Arquivos mais afetados (sed + awk)
echo ""
echo "=== ARQUIVOS MAIS CITADOS ==="
grep -oP '/[\w/]+\.\w+' "$LOG" 2>/dev/null |
    sort |
    uniq -c |
    sort -rn |
    head -5 |
    awk '{printf "  %-50s %d\n", $2, $1}'
SCRIPT

chmod +x relatorio-log.sh

# Testar com dados de exemplo
cat > sample.log << 'EOF'
192.168.1.1 - - [01/Jan/2024:10:30:15] "GET /index.html" 200 1234
192.168.1.2 - - [01/Jan/2024:10:31:20] "POST /api" 500 0 ERROR: timeout
10.0.0.1 - - [01/Jan/2024:11:00:05] "GET /health" 200 OK
192.168.1.1 - - [01/Jan/2024:11:05:30] "GET /api/data" 500 ERROR: db connection
10.0.0.2 - - [01/Jan/2024:12:15:00] "GET /index.html" 304
EOF

./relatorio-log.sh sample.log

grep busca padrões, sed transforma streams, awk processa colunas e agrega. Juntas com sort, uniq, find e cut formam o arsenal essencial de processamento de texto no Unix.