Tutoriais/Sed
De UnderLinux Wiki
Tabela de conteúdo |
Introdução
Este tutorial conterá dicas diversas de como usar o sed para problemas cotidianos e na confecção de scripts
O propósito deste manual é despertar sua curiosidade para esta poderosa ferramenta, aconselhamos seguir os links no final deste tutorial para aprofundar seus conhecimentos sobre o assunto.
Definição
O programa SED é um editor de Streams, mas o que são streams afinal?
Pense no tráfego de dados como água em um encanamento, agora pense que ao invés de gotas temos bits e que o cano é bem fino e que só deixa passar um bit de cada vez. Isso é uma Stream, bits enfileirados. Quando temos uma Stream em rede o nome muda, temos aí um Socket.
O SED é interativo?
Imagine o editor [Vim] ou o Emacs, eles são editores interativos, pelos quais vamos abrindo os arquivos a serem editados e então vamos interativamente, modificando o que queremos. O SED por sua vez pode apagar, por exemplo, a terceira linha de mil arquivos sem que tenhamos de abrir um a um, é por isso que dizemos que o SED é um editor não interativo.
Expressões Regulares
O SED pode usar Expressões regulares nos seus padrões de inserção ou substituição. Veja exemplos:
Para ler um arquivo de configuração qualquer, ocultando as linhas que iniciam com "#", ou seja, linhas comentadas sed -e '/^#/d' /etc/services | less
O circunflexo, em expressões regulares, significa início de linha!
Exemplos de uso do SED
Veja o script abaixo, nele a cor branca bgcolor="#white" foi modificada para um cinza bgcolor="#eeeeee" e as modificações são aplicadas e então são criados os arquivos com as modificações no diretório pessoal ~/tmp.
#!/bin/bash # Scrip para mudar background de arquivos html # Criado: Sex 30/Dez/2005 hs 22:12 # ultima modificação: Sex 30 Dez 2005 22:15:06 BRST for i in *.html do cat $i | sed 's/bgcolor="#white"/bgcolor="#eeeeee"/' > ~/tmp/$i done
Administradores de sistemas podem e devem usar o SED, pois isto poupa tempo. O scrip acima foi criado só para que a leitura de um manual (que tinha o background branco) ficasse menos cansativa.
cat arquivo.txt | sed '4q;d'
Substituições de texto (substituir as vírgulas [,] por hífens[-]):
cat arquivo.txt | sed 's/,/-/g'
Pegar somente o endereço de retorno
nslookup a.root-servers.net | tail -n 1 | sed s/Address:\ \ \ \ //
Colocar número de linhas de um arquivo em uma variável
var=`sed -n '$=' /etc/passwd`
Só para constar, outro modo de contar linhas de um arquivo seria
cat /caminho/para/o/arquivo | wc -l
Substituição
Um comando de substituição tem a seguinte sintaxe:
sed 's/texto_antigo/texto_novo/' arquivo.txt
Pode-se, retirando as aspas simples, que protegem o comando para não ser lido pelo bahs, colocar variáveis de modo que uma construção assim é possível:
sed s/texto_antigo/$var2/ arquivo.txt
as aspas simples são para que o conteúdo do comando seja interpretado pelo sed antes do Bash.
Obs: Não direcione a saída para o próprio arquivo, pois isto irá apaga-lo. Uma solução segura é fazer algo assim:
sed 's/texto_antigo/texto_novo/' arquivo.txt > arquivo2.txt cat arquivo2.txt > arquivo.txt
insere 5 espaços em branco no ínicio de cada linha (faz o "offset" da pagina)
sed 's/^/ /'
substituir (achar e trocar) "foo" por "bar" em cada linha
sed 's/foo/bar/' # troca somente a 1a instância de uma linha sed 's/foo/bar/4' # troca somente a 4a instância de uma linha sed 's/foo/bar/g' # troca TODAS as instâncias de uma linha
substitui "foo" por "bar" SOMENTE nas linhas que contem "baz"
sed '/baz/s/foo/bar/g'
Impressão
IMPRESSÃO SELETIVA DE CERTAS LINHAS:
# imprime as primeiras 10 linhas de um arquivo (emula o comportamento do "head") sed 10q
# imprime a primeira linha de um arquivo (emula o "head -1") sed q
# imprime as últimas 10 linhas de um arquivo (emula o "tail") sed -e :a -e '$q;N;11,$D;ba'
# imprime somente a última linha de um arquivo (emula o "tail -1") sed '$!d'
# imprime somente as linhas que se encaixam na expressão regular # (emula o "grep") sed -n '/regexp/p' # método 1 sed '/regexp/!d' # método 2
# imprime somente as linhas que NÃO se encaixam na regexp (emula o "grep -v") sed -n '/regexp/!p' # método 1, corresponde ao descrito acima sed '/regexp/d' # método 2, sintaxe mais simples
# imprime uma linha de contexto antes e depois da expressão regular,
# com o número da linha indicando onde a expressão regular
# aparece (similar ao "grep -A1 -B1")
sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h
# procura e imprime por AAA e BBB e CCC (em qualquer ordem) sed '/AAA/!d; /BBB/!d; /CCC/!d'
# procura e imprime por AAA e BBB e CCC (nessa ordem) sed '/AAA.*BBB.*CCC/!d'
# procura e imprime por AAA ou BBB ou CCC (emula o "egrep") sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
# imprime um parágrafo se ele possuir AAA (linhas vazias separam os parágrafos).
# Com o HHsed v1.5 deve ser inserido o 'G;' apos o 'x;', nos 3 scripts abaixo
sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;'
# imprime um parágrafo se ele possuir AAA e BBB e CCC (em qualquer ordem)
sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;/BBB/!d;/CCC/!d'
# imprime o parágrafo inteiro se ele possuir AAA ou BBB ou CCC
sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
# imprime somente as linhas com 65 caracteres ou mais
sed -n '/^.\{65\}/p'
# imprime somente as linhas com menos que 65 caracteres
sed -n '/^.\{65\}/!p' # método 1, corresponde ao descrito acima
sed '/^.\{65\}/d' # método 2, sintaxe mais simples
# imprime uma parte do arquivo que vai da expressão regular até # o final do mesmo sed -n '/regexp/,$p'
# imprime uma parte do arquivo baseada nos números das linhas (linhas 8-12, # inclusive) sed -n '8,12p' # método 1 sed '8,12!d' # método 2
# imprime a linha de número 52 sed -n '52p' # método 1 sed '52!d' # método 2 sed '52q;d' # método 3, eficiente com arquivos grandes
# imprime um pedaço de arquivo que está entre as duas # expressões regulares (inclusive) sed -n '/Iowa/,/Montana/p' # é case sensitive
Deleção
# imprime todo o arquivo EXCETO a parte entre 2 expressões regulares sed '/Iowa/,/Montana/d'
# deleta linhas duplicadas de um arquivo (emula o "uniq"). A primeira # linha de um conjunto de linhas duplicadas é mantida, o resto é deletada sed '$!N; /^\(.*\)\n\1$/!P; D'
# deleta TODAS as linhas em branco de um arquivo (o mesmo que "grep '.' ") sed '/^$/d'
# deleta todas as linhas brancas CONSECUTIVAS de um arquivo exceto a primeira;
# ainda deleta todas as linhas em branco do início e fim do arquivo (emula o
# "cat -s")
sed '/./,/^$/!d' # método 1, permite 0 brancos no topo, 1 no
# final do arquivo
sed '/^$/N;/\n$/D' # método 2, permite 1 branco no top, 0 no
# final do arquivo
# deleta todas as linhas em branco do arquivo, exceto as 2 primeiras: sed '/^$/N;/\n$/N;//D'
# deleta todas as linhas em branco iniciais, no início do arquivo sed '/./,$!d'
# deleta todas as linhas em branco finais, no final do arquivo sed -e :a -e '/^\n*$/N;/\n$/ba'
# deleta a última linha de cada parágrafo
sed -n '/^$/{p;h;};/./{x;/./p;}'
Criei um script ao qual dei o nome del_linhas, ele aceita um ou dois parâmetros quando se usar um só parâmetro ele apaga a linha fornecida pelo usuário, quando o script recebe dois parâmetros ele apaga do primeiro até o segundo. Outro detalhe sobre este scrip é que ele atua no diretório corrente.
1 #!/bin/bash
2 # Criado em:Qua 04/Jan/2006 hs 16:51
3 # ultima modificação: Qui 05 Jan 2006 10:13:51 BRST
4 # Apaga linhas
5 # Autor: Sérgio Luiz Araújo Silva
6
7 if [ "$#" -lt "2" ]
8 then # se for passado só uma linha
9 for i in `ls *`
10 do
11 echo "apagando a linha $1 do arquivo $i..."
12 cat $i | sed ${1}d > /tmp/$i
13 cat /tmp/$i > $i
14 rm -f /tmp/$i
15 done
16 else # se for passado um intervalo
17 for i in `ls *`
18 do
19 echo "apagando valo de $1 até $2"
20 cat $i | sed ${1},${2}d > /tmp/$i
21 cat /tmp/$i > $i
22 rm -f /tmp/$i
23 done
24 fi
Os fragmentos do scrip relevantes para o estudo do SED são (na linha 12)
sed ${1}d
e na linha 20
sed ${1},${2}d
Ambos usam variáveis e portanto não se usa aspas simples. A variável
${1}
É o primeiro parâmetro passado para o scrpt e a variável
${2}
É o segundo parâmetro passado para o script.
Script para geração de arquivos
Para estudar o sed fiz um scrip que cria 100 arquivos com a data atual
caso queira pode trocar o comando de data (date) por uma string qualquer por exemplo:
echo "string que será inserida no arquiovo"
#!/bin/bash # Criado em:Seg 02/Jan/2006 hs 21:16 # ultima modificação: Qua 04 Jan 2006 18:16:10 BRST # Script para criação de arquivos em série # Autor: Sérgio Luiz Araújo Silva # http://vivaotux.blogspot.com # # criação da data por extenso do tipo # seguna 2 de janeiro de 2006 # trechos precedidos com "#" não são interpretados pelo bash # dia_semana=`date +%A` dia_mes=`date +%d` mes=`date +%B` ano=`date +%Y` hora=`date +%T` dia_hoje=`echo "$dia_semana $dia_mes de $mes de $ano"` # # insira arquí o texto que vai ser colocado em cada arquivo # se for usar aspas " proteja com contra barra "\" # texto="Este deve ser usado para estudos diversos, experimente usar o SED para fazer modificações em todos os arquivos de uma só vez, visite a página do Aurélio \"http://aurelio.net/\" vá na seção sobre o sed \"http://aurelio.net/sed/\". Não deixe também de visitar o manual sobre Expressões regulares \"http://guia-er.sourceforge.net/guia-er.html\" visite o site do Thobias ele é o mantenedor das funções zz criadas pelo Aurélio a página do Thobias é: \"http://www.thobias.org/\" e as funções zz você encontra em: \"http://funcoeszz.net/\" e é claro visite meu blog: \"http://vivaotux.blogspot.com\" outro bom site é Sed por exemplos: \"http://geocities.yahoo.com.br/cesarakg/sed-1.html\" \"http://geocities.yahoo.com.br/cesarakg/sed-2.html\" \"http://geocities.yahoo.com.br/cesarakg/sed-3.html\" Como o próprio nome diz, estes links dão acesso a uma página com dezenas de exemplos práticos sobre a utilização do SED Para exibir cada um dos arquivos gerados com numeração faça: cat -n arquivo[1-100].txt, onde [1-100] é qualquer um dos 100 arquivos gerados pro este script." # # Coloque aquí o seu nome autor="Sérgio Luiz Araújo Silva" # coloque aquí a extensão dos arquivos extensao=".txt" # o laço propriamente de criação dos arquivos for ((i=1;i<=100;i++)) do touch arquivo$i.txt echo " " >> arquivo$i$extensao #logo abaixo o cabeçalho echo " =====================================================" >> arquivo$i$extensao echo " Arquivo$i$extensao" >> arquivo$i$extensao echo " Criado $dia_hoje horas $hora" >> arquivo$i$extensao echo " Autor: $autor" >> arquivo$i$extensao echo " Arquivo para testes com SED ou egrep" >> arquivo$i$extensao echo " ou qualquer outro propósito, lhe convier!" >> arquivo$i$extensao echo " =====================================================" >> arquivo$i$extensao echo " " >> arquivo$i$extensao echo "$texto" >> arquivo$i$extensao # neste ponto insiro o texto echo " " >> arquivo$i$extensao done
Miscelânia
Para quem usa o vim há algumas configurações úteis a serem inseridas no .vimrc
Cria um cabeçalho para scripts bash
fun! InsertHeadBash()
normal(1G)
call append(0, "#!/bin/bash")
call append(1, "# Criado em:" . strftime("%a %d/%b/%Y hs %H:%M"))
call append(2, "# ultima modificação: " . strftime("%a %d/%b/%Y hs %H:%M"))
call append(3, "# Autor: Sérgio Luiz Araújo Silva ")
call append(3, "# Propósito do script")
normal($)
endfun
map ,sh :call InsertHeadBash()<cr>A
Para chamar o mapeamento acima digite ,sh
Cria um registro de alterações de arquivo
" ChangeLog entry convenience
" Função para inserir um status do arquivo
" cirado: data de criação, alteração, autor etc
fun! InsertChangeLog()
normal(1G)
call append(0, "Arquivo")
call append(1, "Criado: " . strftime("%a %d/%b/%Y hs %H:%M"))
call append(2, "ultima modificação: " . strftime("%a %d/%b/%Y hs %H:%M"))
call append(3, "Autor: Sérgio Luiz Araújo Silva")
normal($)
endfun
map ,cl :call InsertChangeLog()<cr>A
Para chamar o mapeamento acima digite ,cl
Insere número de linhas no arquivo atual
map ,n :%s/^/\=line('.'). ' ' <cr>
Para chamar o mapeamento acima digite ,n
Lembre-se que a insereção do número de linhas é para o caso de enviar o script para análise, setar o número de linhas para ser exibido apenas é feito assim:
" o mapeamento abaixo coloca e retira a numeração " o sistema alterna a numeração para ativa ou desativada (boleano) map <F11> <esc>:set nu!<cr>
Veja mais sobre o vimrc em http://aurelio.net/doc/vim/


