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.