Visite também: BR-Linux ·  VivaOLinux ·  LinuxSecurity ·  Dicas-L ·  NoticiasLinux ·  SoftwareLivre.org ·  [mais]

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/

Veja Também