Qual é o significado de: a; $! N; em um comando sed?

6
$ (echo hello; echo there) | sed ':a;$!N;s/\n/string/;ta'
hellostringthere

Acima do comando sed substitui o novo caractere de linha pela string "string". Mas eu não sei o significado de :a;$!N;s/\n/string/;ta entre aspas simples. Eu sei a parte do meio s/\n/string/ . Mas eu não sei a função da primeira ( :a;$!N; ) e da última parte ( ta ).

    
por Avinash Raj 05.05.2014 / 17:50

2 respostas

12

Estes são os comandos, reconhecidamente enigmáticos, sed . Especificamente (de man sed ):

  

: label
  Rótulo para os comandos b e t.

     

t label
  Se um s /// fez uma substituição bem sucedida desde o último                 linha de entrada foi lida e desde o último comando t ou T, então                 ramo para rotular; se label for omitido, ramifique para o final do script.

     

n N Leia / acrescente a próxima linha de entrada no espaço padrão.

Assim, o script que você postou pode ser dividido em (espaços adicionados para legibilidade):

sed ':a;  $!N;  s/\n/string/;  ta'
     ---  ----  -------------  --
      |     |        |          |--> go back ('t') to 'a'
      |     |        |-------------> substitute newlines with 'string'
      |     |----------------------> If this is not the last line ('$!'), append the 
      |                              next line to the pattern space.
      |----------------------------> Create the label 'a'.

Basicamente, o que isso está fazendo poderia ser escrito em pseudocódigo como

while (not end of line){
    append current line to this one and replace \n with 'string'
}

Você pode entender isso um pouco melhor com um exemplo de entrada mais complexo:

$ printf "line1\nline2\nline3\nline4\nline5\n" | sed ':a;$!N;s/\n/string/;ta'
line1stringline2stringline3stringline4stringline5

Não sei ao certo por que o !$ é necessário. Tanto quanto eu posso dizer, você pode obter a mesma saída com

printf "line1\nline2\nline3\nline4\nline5\n" | sed ':a;N;s/\n/string/;ta'
    
por terdon 05.05.2014 / 18:07
1

Não há referência "man" para esses comandos (bem, na verdade apenas um "lembrete") então você deve consultar as páginas de informações, especificamente a seção "3.7 Comandos para gurus 'sed'". Há também um aviso de que você deveria considerar awk ou Perl até esse ponto, e eles estão corretamente:

  • : LABEL [Nenhum endereço permitido.]

    Especifique a localização de LABEL para os comandos de ramificação. Em todos os outros  respeitos, um não-op.

    neste exemplo específico, basta criar um rótulo, não é operacional. O a pode ser alterado para o que você quiser, desde que seja igual a t label.

  • ; Os comandos dentro de um SCRIPT ou SCRIPT-FILE podem ser separados por ponto e vírgula (';') ou novas linhas (ASCII 10).

    Neste caso, é para separar comandos diferentes, eles podem ser facilmente substituídos por novas linhas.

  • $!N Verifique se não é a última linha.

  • s/\n/string/ sua expressão regex. Não há nada importante aqui.
  • t LABEL  Ramifique para LABEL somente se houver um s ubstitution bem-sucedido  desde a última linha de entrada foi lida ou ramificação condicional foi tomada.  O LABEL pode ser omitido, caso em que o próximo ciclo é iniciado.

    Isso verifica onde ocorreu a substituição do comando (a expressão regex) e executa o que estiver no rótulo; nesse caso, o rótulo é a .

Se tivermos que explicar o comando inteiro com o inglês simples, será igual a:

Crie e armazene uma verificação para a última linha no rótulo a , substitua todas as "novas linhas" por string e pare quando a verificação do rótulo a for verdadeira.

    
por Braiam 05.05.2014 / 18:19