O conceito de 'espaço de espera' e 'espaço de padrão' no sed

86

Estou confuso com os dois conceitos em sed: espaço de retenção e espaço de padrão. Alguém pode ajudar a explicá-los?

Aqui está um snippet do manual:

h H    Copy/append pattern space to hold space.
g G    Copy/append hold space to pattern space.

n N    Read/append the next line of input into the pattern space.

Esses seis comandos realmente me confundem.

ChenQi
fonte
4
Experimente você mesmo:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
choroba,
4
Não se confunda, apenas não os use. Para qualquer coisa além de simples substituições em uma única linha, você deve usar awk, não sed. Espaços de retenção, espaços de padrão e 95% das construções da linguagem sed foram inventados antes do awk, quando não havia alternativa melhor. Eles se tornaram obsoletos assim que o awk foi inventado em meados da década de 1970 e só são mantidos vivos hoje por pessoas que gostam de resolver problemas usando a sintaxe arcana do seds, em vez de fazê-lo de maneira simples e clara no awk. Se você estiver usando mais do que s, g e p (com -n) no sed, é quase certo que está usando a ferramenta errada.
Ed Morton
26
Morton awk trabalha com dados estruturados (cada linha tem a mesma estrutura). O Sed deve funcionar com dados aleatórios brutos. Portanto, você não pode simplesmente usar awk em vez de sed.
Pithikos de
5
Eu recomendo fortemente a leitura info sed. É muito mais detalhado do que a página de manual simples.
Fernando Basso
4
Eu concordo com Pithikos. Desci a pista como Morton fez, e me fiz a mesma pergunta que Morton fez. No entanto, eu ainda não poderia descartar o sed tão facilmente.
eigenfield

Respostas:

111

Quando sed lê um arquivo linha por linha, a linha que foi lido no momento é inserido no padrão de buffer (espaço padrão). O buffer de padrão é como o buffer temporário, o bloco de notas onde as informações atuais são armazenadas. Quando você diz ao sed para imprimir, ele imprime o buffer padrão.

O buffer de retenção / espaço de retenção é como um armazenamento de longo prazo, de forma que você pode capturar algo, armazená-lo e reutilizá-lo mais tarde quando o sed estiver processando outra linha. Você não processa diretamente o espaço de retenção; em vez disso, você precisa copiá-lo ou anexar ao espaço do padrão se quiser fazer algo com ele. Por exemplo, o comando de impressão pimprime apenas o espaço do padrão. Da mesma forma, sopera no espaço padrão.

Aqui está um exemplo:

sed -n '1!G;h;$p'

(a opção -n suprime a impressão automática de linhas)

Há três comandos aqui: 1!G, he $p. 1!Gtem um endereço, 1(primeira linha), mas !significa que o comando será executado em todos os lugares, exceto na primeira linha. $ppor outro lado, só será executado na última linha. Então o que acontece é o seguinte:

  1. a primeira linha é lida e inserida automaticamente no espaço do padrão
  2. na primeira linha, o primeiro comando não é executado; hcopia a primeira linha para o espaço de espera .
  3. agora a segunda linha substitui tudo o que estava no espaço do padrão
  4. na segunda linha, primeiro executamos G, anexando o conteúdo do buffer de retenção ao buffer de padrão, separando-o por uma nova linha. O espaço padrão agora contém a segunda linha, uma nova linha e a primeira linha.
  5. Em seguida, o hcomando insere o conteúdo concatenado do buffer de padrão no espaço de retenção, que agora contém as linhas reversas dois e uma.
  6. Prosseguimos para a linha número três - vá para o ponto (3) acima.

Finalmente, depois que a última linha foi lida e o espaço de retenção (contendo todas as linhas anteriores em ordem reversa) foi anexado ao espaço do padrão, o espaço do padrão é impresso p. Como você já deve ter adivinhado, o procedimento acima faz exatamente o que o taccomando faz - imprime o arquivo ao contrário.

janeiro
fonte
3
As opções G e h funcionam como "cortar e acrescentar" ?? Não se parece com a operação "copiar e anexar".
Sorriso
O que acrescenta com padrão e mantém o espaço quando comandos aninhados (chaves) são usados? '195,210{/add/p}'… É possível extrair a última linha de um grupo de linhas envolvidas em um padrão?
Sandburg
17

@Ed Morton: Eu discordo de você aqui. Achei sedmuito útil e simples (depois de entender o conceito do padrão e manter os buffers) criar uma maneira elegante de fazer o grep de várias linhas.

Por exemplo, vamos pegar um arquivo de texto que contém nomes de host e algumas informações sobre cada host, com muito lixo entre as quais eu não me importo.

Host: foo1
some junk, doesnt matter
some junk, doesnt matter
Info: about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Info: a second line about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Host: foo2
some junk, doesnt matter
Info: about foo2 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter

Para mim, um script awk para obter apenas as linhas com o nome do host e a infolinha correspondente levaria um pouco mais do que sou capaz de fazer com o sed:

sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt

a saída se parece com:

Host: foo1
Info: about foo1 that I really care about!!
Host: foo1
Info: a second line about foo1 that I really care about!!
Host: foo2
Info: about foo2 that I really care about!!

(Observe que Host: foo1aparece duas vezes na saída.)

Explicação:

  1. -n desativa a saída, a menos que seja explicitamente impresso
  2. primeira correspondência, encontra e coloca a Host:linha no buffer de retenção (h)
  3. segunda correspondência, encontra a próxima linha Info:, mas primeiro troca (x) a linha atual no buffer de padrão com o buffer de retenção e imprime (p) a Host:linha, depois troca novamente (x) e imprime (p) a linha Info :.

Sim, este é um exemplo simplista, mas eu suspeito que este seja um problema comum que foi rapidamente resolvido por uma linha simples do sed. Para tarefas muito mais complexas, como aquelas nas quais você não pode confiar em uma determinada sequência previsível, o awk pode ser mais adequado.

Jens Jensen
fonte
2
Neste caso, você pode apenas usar grep:grep 'Host\|Info'
Pithikos
Se houver duas linhas de informação após um determinado host, @JensJenson deseja que ambas as linhas de informação sejam precedidas por uma linha de informação. Acho que vou editar a resposta de acordo. Pithikos, grep não será suficiente.
Aaron McDaid
3
@JensJenson, o awkequivalente ao seu código sed é bem curto também:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Aaron McDaid
11

Embora a resposta de @janeiro e o exemplo sejam bons, a explicação não foi suficiente para mim. Tive que pesquisar e aprender muito até conseguir entender exatamente como sed -n '1!G;h;$p'funciona. Portanto, gostaria de elaborar o comando para alguém como eu.

Em primeiro lugar, vamos ver o que o comando faz.

$ echo {a..d} | tr ' ' '\n' # Prints from 'a' to 'd' in each line
a
b
c
d
$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;$p'
d
c
b
a

Inverte a entrada como tac comando faz.

sedlê linha por linha, então vamos ver o que acontece no espaço do patten e no espaço de espera em cada linha. Como o hcomando copia o conteúdo do espaço do padrão para o espaço de retenção, ambos os espaços têm o mesmo texto.

Read line    Pattern Space / Hold Space    Command executed
-----------------------------------------------------------
a            a$                            h
b            b\na$                         1!G;h
c            c\nb\na$                      1!G;h
d            d\nc\nb\na$                   1!G;h;$p

Na última linha, $pimprime d\nc\nb\na$que é formatado para

d
c
b
a

Se quiser ver o espaço do padrão para cada linha, você pode adicionar um lcomando.

$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;l;$p'
a$
b\na$
c\nb\na$
d\nc\nb\na$
d
c
b
a

Achei muito útil assistir a este tutorial em vídeo Entendendo como funciona o sed , pois o cara mostra como cada espaço será utilizado passo a passo. O hold spaced é referido no 4º tutorial, mas recomendo assistir a todos os vídeos se você não estiver familiarizado sed.

Também o documento GNU sed e o tutorial Sed de Bruce Barnett são referências muito boas.

Sanghyun Lee
fonte
2
Acho que também será útil mencionar que o espaço de retenção para todos os fins práticos está vazio, a menos que adicionemos algo a ele.
Naveed