Ubuntu sed
O sed (de stream editor, "editor de fluxo") edita texto sem abrir o arquivo num editor. Você descreve a mudança, como "troque 8080 por 9090", e ele aplica em todas as linhas. É a ferramenta para mudar configurações em scripts e fazer substituições em massa.
- Como o sed processa o texto, linha por linha.
- A ler e escrever o comando
s/procurar/trocar/parte por parte. - A diferença entre mostrar o resultado e alterar o arquivo (
-i), e como fazer backup antes. - A escolher linhas (endereços), apagar, imprimir e inserir linhas.
O simulador do guia não tem o sed. Os exemplos abaixo são sessões reais do Ubuntu 26.04 (GNU sed 4.9); para praticar, abra o seu terminal.
Como o sed trabalha
O sed lê o texto uma linha por vez. Para cada linha, aplica os seus comandos e escreve o resultado na saída. Depois passa para a próxima linha.
arquivo ──► [ lê a linha ] ──► [ aplica os comandos ] ──► [ escreve na tela ] ──┐
▲ │
└────────────────────── próxima linha ◄────────────────────────┘
Duas consequências importantes:
- Por padrão, o resultado vai para a tela. O arquivo original não muda.
- O sed funciona com arquivos de qualquer tamanho, porque nunca carrega o arquivo inteiro.
Nos exemplos, vamos usar estes dois arquivos:
$ cat frutas.txt banana maçã laranja banana uva $ cat app.conf caminho=/home/ana/fotos porta=8080 # comentário nome=loja
Substituir texto: o comando s
O uso mais comum do sed é substituir. Primeiro, a linha inteira:
- sed
- O programa.
- 's/banana/BANANA/'
- O script, ou seja, a instrução para o sed. Fica entre aspas simples para o shell não mexer nos símbolos.
- frutas.txt
- O arquivo a ler. Sem ele, o sed lê a entrada (por exemplo, de um pipe).
Agora, o script por dentro. As barras / separam as partes:
- s
- O comando: substitute, "substituir".
- /banana
- O que procurar. Pode ser uma expressão regular.
- /BANANA
- Pelo que trocar.
- /
- Fim. Depois da última barra vêm as flags (opcionais), como
g.
$ sed 's/banana/BANANA/' frutas.txt BANANA « trocou maçã laranja BANANA « trocou uva $ cat frutas.txt banana « o arquivo continua igual maçã laranja banana uva
Todas as ocorrências da linha: flag g
Sem flag, o sed troca só a primeira ocorrência de cada linha. A flag g (de global) troca todas:
$ echo "a a a" | sed 's/a/X/' X a a $ echo "a a a" | sed 's/a/X/g' X X X $ echo "a a a" | sed 's/a/X/2' a X a « um número troca só a 2ª ocorrência
Ignorar maiúsculas e minúsculas: flag I
$ echo "Olá Mundo" | sed 's/mundo/Ubuntu/' Olá Mundo « não achou: "mundo" ≠ "Mundo" $ echo "Olá Mundo" | sed 's/mundo/Ubuntu/I' Olá Ubuntu
Alterar o arquivo de verdade: -i
A opção -i (de in-place, "no lugar") grava o resultado no próprio arquivo, em vez de mostrar na tela.
- sed
- O programa.
- -i.bak
- Edita o arquivo e guarda uma cópia do original com a extensão
.bak. - 's/porta=8080/porta=9090/'
- A substituição.
- app.conf
- O arquivo que será alterado.
$ sed -i.bak 's/porta=8080/porta=9090/' app.conf $ ls app.conf app.conf.bak frutas.txt $ grep porta app.conf app.conf.bak app.conf:porta=9090 « o arquivo mudou app.conf.bak:porta=8080 « a cópia guarda o original
-i
Rode primeiro sem -i e confira a saída. Só depois acrescente o -i. Com -i sozinho (sem .bak), não existe "desfazer".
Formas diferentes de salvar o resultado
| Forma | Exemplo | Quando usar |
|---|---|---|
| Só olhar | sed 's/a/b/' arq | Sempre, primeiro, para testar. |
| Novo arquivo | sed 's/a/b/' arq > novo | Quer manter o original intacto. |
| No lugar, com backup | sed -i.bak 's/a/b/' arq | Mudança em configuração: dá para voltar. |
| No lugar, sem backup | sed -i 's/a/b/' arq | Scripts testados, arquivos que você pode recriar. |
sed … arq > arq
O > esvazia o arquivo antes de o sed ler, e você perde tudo. Veja Redirecionamento.
Caminhos com barras: troque o separador
Para trocar /home/ana por /home/bia, as barras do caminho se misturam com as barras do comando:
$ sed 's//home/ana//home/bia/' app.conf sed: -e expressão #1, caractere 9: opção desconhecida para o comando `s' (s///?)
O sed aceita qualquer caractere como separador: é o primeiro que aparece depois do s. Use | ou #:
$ sed 's|/home/ana|/home/bia|' app.conf caminho=/home/bia/fotos porta=8080 # comentário nome=loja
Outra forma é "escapar" cada barra com \: sed 's/\/home\/ana/\/home\/bia/'. Funciona, mas é bem mais difícil de ler.
Padrões: expressões regulares
O que você procura pode ser um padrão, e não só um texto fixo. Os símbolos mais usados:
| Símbolo | Significa | Exemplo |
|---|---|---|
. | Qualquer caractere | c.sa casa com "casa" e "cosa" |
* | O anterior, zero ou mais vezes | porta=.* = "porta=" e qualquer coisa depois |
^ | Começo da linha | ^# = linha que começa com # |
$ | Fim da linha | ^$ = linha vazia |
[0-9] | Um caractere da lista | [0-9] = um dígito |
\+ | O anterior, uma ou mais vezes | [0-9]\+ = um número |
& | Na troca: o texto encontrado | s/[0-9]\+/[&]/ põe o número entre colchetes |
Trocar o valor da porta, seja ele qual for:
$ sed 's/porta=.*/porta=9090/' app.conf caminho=/home/ana/fotos porta=9090 # comentário nome=loja $ sed 's/[0-9]\+/[&]/' app.conf | head -n 2 caminho=/home/ana/fotos porta=[8080] « & repetiu o número encontrado
Grupos e expressões estendidas: -E
Com -E, você usa parênteses para guardar partes do que foi encontrado e devolvê-las com \1, \2…
$ sed -E 's/([a-z]+)=(.*)/\2 <- \1/' app.conf | head -n 2 /home/ana/fotos <- caminho 8080 <- porta
Aqui, ([a-z]+) guardou o nome (grupo 1) e (.*) guardou o valor (grupo 2). Na troca, eles foram escritos em ordem invertida.
Escolher linhas: endereços
Antes de um comando, você pode dizer em quais linhas ele vale. Isso se chama endereço.
| Endereço | Linhas | Exemplo |
|---|---|---|
3 | Só a linha 3 | sed '3s/a/b/' |
2,4 | Da 2 à 4 | sed '2,4d' |
$ | A última | sed '$d' |
/padrão/ | As que contêm o padrão | sed '/porta/s/8080/80/' |
/ini/,/fim/ | De uma linha com "ini" até uma com "fim" | sed -n '/\[db\]/,/^$/p' |
Apagar linhas: d
O comando d (de delete) não escreve a linha. Um uso muito comum é ver uma configuração sem comentários nem linhas vazias:
- sed
- O programa.
- '/^#/d
- Apaga as linhas que começam com
#… - ;
- …e depois (o ponto e vírgula separa comandos)…
- /^$/d'
- …apaga as linhas vazias.
- app.conf
- O arquivo.
$ sed '/^#/d; /^$/d' app.conf caminho=/home/ana/fotos porta=8080 nome=loja
Imprimir só algumas linhas: -n + p
A opção -n desliga a impressão automática. O comando p (de print) imprime a linha. Juntos, mostram só o que você escolheu:
$ sed -n '2,3p' frutas.txt maçã laranja $ sed -n '/porta/p' app.conf porta=8080 $ sed -n '$=' app.conf 5 « o comando = mostra o número da linha; aqui, a última
Inserir e acrescentar linhas: i e a
$ sed '1i # Arquivo gerado automaticamente' app.conf | head -n 2 # Arquivo gerado automaticamente caminho=/home/ana/fotos $ sed '$a debug=false' app.conf | tail -n 2 nome=loja debug=false
1i insere antes da linha 1; $a acrescenta depois da última.
Formas diferentes de ver um arquivo sem comentários
| Comando | Observação |
|---|---|
sed '/^#/d; /^$/d' arq | Um programa só, dois comandos. |
grep -v '^#' arq | grep -v '^$' | Mais fácil de ler para iniciantes. |
grep -Ev '^(#|$)' arq | O mais curto. Veja grep. |
awk '!/^#/ && NF' arq | Com awk: não começa com # e tem algum campo. Veja awk. |
Quando não funciona
$ sed 's/banana/BANANA' frutas.txt sed: -e expressão #1, caractere 15: comando `s' não terminado
O que aconteceu: faltou a última barra. O comando s precisa de três: s/a/b/.
Como resolver: sed 's/banana/BANANA/' frutas.txt. O número do "caractere" diz onde o sed se perdeu.
O que aconteceu: o texto tem barras /, como um caminho, e o sed achou que o comando acabou antes.
Como resolver: troque o separador: sed 's|/opt/app|/srv/app|'.
-i
$ sed -i 's/notebook/estudio/' /etc/hostname sed: não foi possível abrir o arquivo temporário /etc/sedrkIxCO: Permissão negada
O que aconteceu: o -i cria um arquivo temporário na mesma pasta e depois troca pelo original. Em /etc, só o root pode criar arquivos.
Como resolver: teste sem -i, depois rode com sudo e backup: sudo sed -i.bak 's/notebook/estudio/' /etc/hostname.
O que aconteceu: o padrão não casou. As causas mais comuns são maiúsculas diferentes ("Mundo" × "mundo"), espaços a mais, ou símbolos especiais. Por exemplo, o ponto em s/1.0/2.0/ significa "qualquer caractere".
Como confirmar: procure o padrão com grep antes: grep -n 'mundo' arquivo. Se o grep não acha, o sed também não vai achar.
Como resolver: use a flag I para ignorar maiúsculas, e escape símbolos especiais com \: s/1\.0/2.0/.
$ novo=9090 $ sed 's/8080/$novo/' app.conf | grep porta porta=$novo
O que aconteceu: dentro de aspas simples, o shell não troca variáveis.
Como resolver: use aspas duplas: sed "s/8080/$novo/" app.conf.
Referência
Opções
| Opção | Descrição | Exemplo |
|---|---|---|
-n | Não imprime automaticamente (use com p) | sed -n '5p' arq |
-i | Edita o próprio arquivo | sed -i 's/a/b/' arq |
-i.bak | Edita e guarda o original como .bak | sed -i.bak 's/a/b/' arq |
-E | Expressões regulares estendidas (+ ? ( ) | sem barra) | sed -E 's/(a|b)+/X/' |
-e | Acrescenta um comando (pode repetir) | sed -e 's/a/b/' -e '/^$/d' |
Comandos
| Comando | Descrição | Exemplo |
|---|---|---|
s/a/b/ | Substitui a primeira ocorrência por linha | sed 's/http/https/' |
s/a/b/g | Substitui todas | sed 's/ /_/g' |
s/a/b/I | Ignora maiúsculas | sed 's/erro/ERRO/I' |
d | Apaga a linha | sed '/^#/d' |
p | Imprime a linha | sed -n '/erro/p' |
i texto | Insere antes | sed '1i Título' |
a texto | Acrescenta depois | sed '$a Fim' |
= | Mostra o número da linha | sed -n '$=' |
q | Sai (para de ler) | sed '10q' (como head) |
y/abc/ABC/ | Troca caractere por caractere | sed 'y/abc/ABC/' |
Exercícios
1. Trocar todas
Complete para trocar todos os espaços de cada linha por _:
2. Editar com segurança
Complete para alterar app.conf no lugar, guardando uma cópia .bak:
3. Só as linhas de erro
Complete para mostrar só as linhas que contêm "erro":
4. Você rodou sed 's/8080/9090/' app.conf e a tela mostrou "porta=9090". O que aconteceu com o arquivo?
5. Qual comando troca o caminho /var/www por /srv/www sem dar erro?
Resumo
- O sed lê linha por linha e mostra o resultado; o arquivo só muda com
-i. s/procurar/trocar/troca a primeira ocorrência por linha;gtroca todas.- Teste sem
-i, depois use-i.bakpara ter como voltar. - Com barras no texto, troque o separador:
s|a|b|. - Endereços escolhem linhas;
dapaga,-n … pimprime só o que você quer.