I/O, Redirecionamento e Pipes
Aula 3 de 6
File Descriptors
Cada processo no Unix tem três streams padrão:
| FD | Nome | Descrição | Direção |
|---|---|---|---|
| 0 | stdin | Entrada padrão | leitura |
| 1 | stdout | Saída padrão | escrita |
| 2 | stderr | Saída de erro | escrita |
Redirecionamento
Redirecionamento de saída
# stdout para arquivo (sobrescreve)
ls > lista.txt
# stdout para arquivo (append)
echo "nova linha" >> log.txt
# stderr para arquivo
comando_que_erro 2> erros.log
# stderr append
comando_que_erro 2>> erros.log
# stdout e stderr para o mesmo arquivo
comando &> tudo.log # bash 4+ (recomendado)
comando > tudo.log 2>&1 # clássico POSIX
# Descartar saída
comando > /dev/null 2>&1
comando &> /dev/null # bash 4+
Redirecionamento de entrada
# stdin de arquivo
wc -l < arquivo.txt
# Here Document (heredoc)
cat << EOF
Linha 1
Linha 2
EOF
# Heredoc com variáveis expandidas
user="Maria"
cat << EOF
Olá, $user!
EOF
# Heredoc sem expansão (quote o delimitador)
cat << 'EOF'
$HOME não será expandido
EOF
# Heredoc com indentação (<<-)
if true; then
cat <<- EOF
Linha indentada com tab
EOF
fi
Here String
# Passar string como stdin
wc -w <<< "Uma string qualquer"
grep "palavra" <<< "buscando palavra aqui"
Pipes
Pipe simples
# Conectar stdout de um comando ao stdin do próximo
ls -la | grep ".md" | wc -l
# Pipeline de visualização
ps aux | grep nginx | awk '{print $2}'
tee — Dividir saída
# Escrever em arquivo E manter no pipeline
echo "dados" | tee arquivo.txt | wc -c
# Append com -a
echo "mais dados" | tee -a arquivo.txt
# Escrever em múltiplos arquivos
ls | tee arquivo1.txt arquivo2.txt | head
pipefail
# Por padrão, o pipeline retorna o exit code do último comando
false | true
echo $? # 0
# Com pipefail, retorna o primeiro não-zero
set -o pipefail
false | true
echo $? # 1
# Prática: usar em scripts
set -euo pipefail
Subshells
# Subshell: executa em processo filho
(cd /tmp && pwd) # /tmp
pwd # diretório original
# Subshell não afeta o shell pai
var="fora"
(var="dentro"; echo "$var") # dentro
echo "$var" # fora
Grouping
# Agrupar comandos no mesmo shell (não cria subshell)
{ echo "um"; echo "dois"; } | grep um
# Redirecionar grupo
{
echo "Início"
ls -la
echo "Fim"
} > relatorio.txt
Process Substitution
# <(cmd): tratar saída como arquivo
diff <(ls dir1) <(ls dir2)
# >(cmd): tratar entrada como arquivo
echo "dados" > >(gzip > dados.gz)
# Exemplo prático: comparar saída de comandos
diff <(curl -s http://site1.com/api) <(curl -s http://site2.com/api)
xargs
# Básico: construir e executar comandos
echo "um dois três" | xargs mkdir
# Placeholder {} (com -I)
find . -name "*.txt" | xargs -I {} cp {} /backup/
# Paralelo com -P
seq 1 10 | xargs -P 4 -I {} sleep {}
# Delimitador nulo (seguro para nomes com espaços)
find . -name "*.txt" -print0 | xargs -0 rm -f
# xargs com substituição posicional
echo "src/*.txt" | xargs -n 1 wc -l
Named Pipes (FIFO)
# Criar pipe nomeado
mkfifo meu_pipe
# Terminal 1: escrever no pipe
echo "dados" > meu_pipe
# Terminal 2: ler do pipe
cat meu_pipe
# Útil para conexão entre processos que não compartilham stdin/stdout
# Limpar
rm meu_pipe
Lab: Pipeline de Análise de Logs
cat > analisa-logs.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail
LOG_DIR="/var/log"
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT
# 1. Coletar logs do sistema (subshell + redirecionamento)
{
echo "=== LOGS DO SISTEMA ==="
sudo journalctl -n 100 --no-pager 2>/dev/null || \
tail -100 /var/log/syslog 2>/dev/null || \
tail -100 /var/log/messages 2>/dev/null
} > "$TEMP_DIR/coletados.log" 2>&1
# 2. Pipeline de análise
echo "=== TOP 10 IPs ==="
grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' "$TEMP_DIR/coletados.log" |
sort |
uniq -c |
sort -rn |
head -10
echo ""
echo "=== ERROS POR HORA ==="
grep -i "error\|fail\|critical" "$TEMP_DIR/coletados.log" |
grep -oE '^[A-Z][a-z]{2} [0-9]{2} [0-9]{2}' |
uniq -c |
sort -rn |
head -10
echo ""
echo "=== COMPARAÇÃO COM LOG ANTERIOR ==="
if [[ -f "$LOG_DIR/logs-anteriores.log" ]]; then
diff <(sort "$TEMP_DIR/coletados.log") <(sort "$LOG_DIR/logs-anteriores.log") || true
fi
# 3. Salvar resultados com tee
echo "Resumo gerado em $(date)" | tee -a "$TEMP_DIR/resumo.txt"
SCRIPT
chmod +x analisa-logs.sh
# Teste
echo "127.0.0.1 - - [01/Jan/2024:10:30:00] error na pagina" | tee test.log
echo "192.168.1.1 - - [01/Jan/2024:11:00:00] failed login" | tee -a test.log
cat test.log | ./analisa-logs.sh
Redirecionamento controla para onde os streams vão. Pipes conectam stdout ao stdin. Process substitution trata comandos como arquivos. xargs paraleliza execuções.