Como o comando sed '1! G; h; $! D' inverte o conteúdo de um arquivo?

20

Minha pergunta está relacionada à sedsolução específica dada nesta resposta para esta questão de grepping reverso . A solução sed/ grepque não consigo decifrar é a seguinte:

sed '1!G;h;$!d' file

Alguém pode decifrar este comando?

Sei pelo VI (M) conhecimento que G denota a última linha do arquivo e que, em caso de estrondo (!) Seguido de um endereço, funciona um pouco assim, grep -vou seja, para dizer que não corresponderá a essa linha. Mas como um todo, o script sed inline acima está além de mim.

Nerd
fonte
3
... muito lentamente ...
mikeserv
11
Como mikeserv sugere. Deve-se notar que esta sedreceita complicada é uma maneira extremamente ineficiente (complexidade O (n ^ 2/2)) de inverter linhas em um arquivo. Seria proibitivamente lento para arquivos com um grande número de linhas. Para uma alternativa de reversão de ordem de linha muito mais eficiente, consulte os taccoreutils do GNU.
Arielf

Respostas:

35

Isso inverte o arquivo linha por linha.

sed '1! G; h; $! d' arquivo

Primeiro, sedpossui um espaço de espera e um espaço de padrão . Temos que distinguir entre eles antes de nos concentrarmos nesse comando específico.

Quando sedlê uma nova linha, ela é carregada no espaço do padrão. Portanto, esse espaço é substituído toda vez que uma nova linha é processada. Por outro lado, o espaço de espera é consistente em todo o processamento e os valores podem ser armazenados lá para uso posterior.


Para o comando:

Existem 3 comandos nesta declaração: 1!G, he$!d

  • 1!Gsignifica que o Gcomando é executado em todas as linhas, exceto na primeira (a !nega a 1). Gsignifica acrescentar o que está no espaço de espera no espaço do padrão.

  • haplica-se a todas as linhas. Ele copia o espaço do padrão para o espaço de espera (e o substitui).

  • $!daplica-se a todas as linhas, exceto a última ( $representa a última linha, !nega-a). dé o comando para excluir a linha (espaço do padrão).


  1. Agora, quando a primeira linha é lida, sedexecuta o hcomando. A primeira linha é copiada no espaço de espera. Em seguida, é excluído, pois corresponde à $!condição. sedcontinua com a segunda linha.
  2. A segunda linha corresponde à condição 1!(não é a primeira linha) e, portanto, o espaço de espera (que tem a primeira linha) é anexado ao espaço do padrão (que tem a segunda linha). Depois disso, no espaço do padrão, agora existe a segunda linha seguida pela primeira, delimitada por uma nova linha. Agora, o hcomando se aplica (como em todas as linhas); tudo o que está no espaço do padrão é copiado para o espaço de espera. A terceira instrução ( $!d) se aplica: A linha é excluída do espaço do padrão.
  3. A etapa 2 agora está concluída com todas as linhas. Passamos para a última linha.
  4. Na última linha ( $), quase toda a Etapa 2 está concluída, mas não a parte de exclusão ( d). sed, quando chamado sem -n, imprime o espaço do padrão automaticamente no final do processamento para cada linha de entrada. Portanto, quando não excluído, o espaço do padrão é impresso. Ele contém agora todas as linhas na ordem inversa .
caos
fonte
11
@ Geek Não, o hcomando copia o espaço do padrão para o espaço de espera, que persiste até o sedfim. Após o final do script, tudo é limpo, porque o binário saiu.
caos
2
Podemos pensar no espaço de espera como registros para o vim? Eles também são numerados? Ou existe apenas um deles?
Geek
2
@ Geek Em sedapenas um espaço de espera. É como uma variável que pode conter algo.
caos
11
@ user1717828 Se não quiséssemos, a primeira linha seria impressa quando processada. Como sed não é invocado -n, temos que excluir todas as linhas, exceto a última. Na última linha, sed acrescenta tudo, desde o espaço de espera até o espaço do padrão. E como o dcomando não será executado, a linha é impressa (esta linha contém agora todo o arquivo invertido).
caos
11
(1) A subquestão / observação do OP (no comentário) é: “portanto, o hcomando na última linha é uma espécie de não operação ”. (Com ênfase adicional e parafraseada ligeiramente). Ele tem razão; quando sedestá processando a última linha de entrada , Glê o espaço de espera (para anexá-lo ao espaço de padrão) e hcopia o espaço de padrão para o espaço de espera, que nunca é referenciado novamente . Poderíamos muito bem dizer sed 'G;$!h;$!d'ou sed 'G;$!{h;d}'. (2) Poderíamos evitar usar ddizendo sed -n 'G;h;$p'.
Scott