Eu concordo com você - provavelmente é um problema genérico. Alguns utilitários comuns têm algumas facilidades para lidar com isso, no entanto.
nl
nl, por exemplo, separa a entrada em páginas lógicas, como -delimitado por um delimitador de seção de dois caracteres . Três ocorrências em uma linha sozinhas indicam o início de um cabeçalho , duas no corpo e uma no rodapé . Ele substitui qualquer um dos encontrados na entrada por uma linha em branco na saída - que são as únicas linhas em branco que já imprime
Alterei seu exemplo para incluir outra seção e inseri-la ./infile. Então fica assim:
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
@@start
line M
line N
line O
@@end
Então eu executei o seguinte:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end$/@@/' <infile |
nl -d@@ -ha -bn -w1
nlpode ser dito para acumular o estado nas páginas lógicas, mas isso não ocorre por padrão. Em vez disso, numerará as linhas de sua entrada de acordo com os estilos e por seção . Então, -hasignifica o número todos os cabeçalho linhas e -bnsignifica há linhas do corpo - como ele começa em um corpo estado.
Até eu aprender isso, costumava usar nlpara qualquer entrada, mas depois de perceber que isso nlpoderia distorcer a saída de acordo com seu -delimitador padrão \:, aprendi a ter mais cuidado com ele e comecei a usar grep -nF ''entradas não testadas. Mas outra lição aprendida naquele dia foi que nlpode ser aplicada de maneira muito útil em outros aspectos - como este - se você apenas modificar um pouco sua entrada - como eu faço sedacima.
RESULTADO
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
Aqui está um pouco mais sobre nl- você notou acima como todas as linhas, exceto as numeradas, começam com espaços? Ao nlnumerar linhas, ele insere um certo número de caracteres na cabeça de cada um. Para essas linhas, ele não é numerado - nem mesmo em branco - ele sempre corresponde ao recuo, inserindo ( -width count + -separator len) * espaços no início das linhas não numeradas. Isso permite reproduzir o conteúdo não numerado exatamente comparando-o com o conteúdo numerado - e com pouco esforço. Quando você considera que nldividirá sua entrada em seções lógicas para você e que pode inserir -ssequências arbitrárias no início de cada linha numerada, fica muito fácil lidar com sua saída:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end/@@/; t
s/^\(@@\)\{1,3\}$/& /' <infile |
nl -d@@ -ha -bn -s' do something with the next line!
'
As impressões acima ...
line A
line B
1 do something with the next line!
line X
2 do something with the next line!
line Y
3 do something with the next line!
line Z
line C
line D
1 do something with the next line!
line M
2 do something with the next line!
line N
3 do something with the next line!
line O
GNU sed
Se nlnão é o seu aplicativo de destino, um GNU sedpode eexecutar um comando shell arbitrário para você, dependendo de uma correspondência.
sed '/^@@.*start$/!b
s//nl <<\\@@/;:l;N
s/\(\n@@\)[^\n]*end$/\1/
Tl;e' <infile
Acima sedcoleta a entrada no espaço do padrão até que tenha o suficiente para passar com êxito a substituição Test e parar a bcriação de gado de volta ao :label. Quando isso ocorre, ele eexecuta nlcom a entrada representada como um <<documento aqui para todo o restante de seu espaço de padrão.
O fluxo de trabalho é assim:
/^@@.*start$/!b
- Se uma
^linha inteira $que !não /coincidir com /o padrão acima, então é bcriados em rancho fora do script e autoprinted - por isso a partir deste ponto estamos apenas trabalhando com uma série de linhas que começou com o padrão.
s//nl <<\\@@/
- o
s//campo vazio /representa o último endereço sedtentado corresponder - portanto, este comando substitui a @@.*startlinha inteira nl <<\\@@.
:l;N
- O
:comando define um rótulo de filial - aqui eu defino um chamado :label. O Ncomando ext anexa a próxima linha de entrada ao espaço do padrão seguido por um \ncaractere ewline. Essa é uma das poucas maneiras de obter uma linha de \new em um sedespaço de padrão - o \ncaractere de ewline é um delimitador seguro para um sedder que faz isso há algum tempo.
s/\(\n@@\)[^\n]*end$/\1/
- essa
s///ubstituição só pode ser bem-sucedida depois que uma partida é encontrada e somente na primeira ocorrência seguinte de uma linha final . Ele atuará apenas em um espaço de padrão no qual a linha de \new final será imediatamente seguida pela @@.*endmarcação do final $do espaço de padrão. Quando ele age, ele substitui toda a cadeia correspondente pelo \1primeiro \(grupo \), ou \n@@.
Tl
- o
Tcomando est ramifica para um rótulo (se fornecido) se uma substituição bem-sucedida não ocorreu desde a última vez que uma linha de entrada foi puxada para o espaço do padrão (como eu faço N) . Isso significa que toda vez que uma linha de \new é anexada ao espaço do padrão que não corresponde ao seu delimitador final, o Tcomando est falha e se ramifica de volta ao :label, o que resulta em sedpuxar a Nlinha ext e girar até obter êxito.
e
Quando a substituição para o jogo final é bem sucedido e o script não suporta a filial de uma falha Test, sedvai execute um comando que looks como este:
nl <<\\@@\nline X\nline Y\nline Z\n@@$
Você pode ver isso editando a última linha lá para parecer Tl;l;e.
Imprime:
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
while ... read
Uma última maneira de fazer isso, e talvez a maneira mais simples, é usar um while readloop, mas por um bom motivo. A concha - (principalmente uma bashconcha) - normalmente é bastante abismal ao lidar com entradas em grandes quantidades ou em fluxos constantes. Isso também faz sentido - o trabalho do shell é manipular caracteres de entrada por caractere e chamar outros comandos que possam lidar com coisas maiores.
Mas, o mais importante é que, em relação ao seu papel, o shell não deve read sobrecarregar muito a entrada - ele é especificado para não armazenar em buffer a entrada ou a saída a ponto de consumir tanto ou não retransmitir o suficiente a tempo de que os comandos que ele chama sejam deixados em falta. - para o byte. Portanto, readé um excelente teste de entrada - para returninformações sobre se há entrada restante e você deve chamar o próximo comando para lê-la - mas, de outra forma, geralmente não é o melhor caminho a percorrer.
Aqui está um exemplo, no entanto, de como alguém pode usar read e outros comandos para processar a entrada em sincronia:
while IFS= read -r line &&
case $line in (@@*start) :;; (*)
printf %s\\n "$line"
sed -un "/^@@.*start$/q;p";;
esac;do sed -un "/^@@.*end$/q;=;p" |
paste -d: - -
done <infile
A primeira coisa que acontece para cada iteração é readpuxar uma linha. Se for bem-sucedido, significa que o loop ainda não atingiu o EOF e, portanto, no casecorrespondente ao delimitador de início, o dobloco é executado imediatamente. Else, printfimprime a $lineele reade sedé chamado.
sedvai print cada linha até encontrar o início marcador - quando qUITS entrada inteiramente. O -uswitch nbuffered é necessário para o GNU sedporque ele pode armazenar um buffer com avidez, mas - de acordo com a especificação - outros POSIX seds devem funcionar sem nenhuma consideração especial - desde que <infileseja um arquivo comum.
Quando o primeiro sed qsai, o shell executa o dobloco do loop - que chama outro sedque imprime todas as linhas até encontrar o marcador final . Ele canaliza sua saída para paste, porque imprime números de linhas cada um em sua própria linha. Como isso:
1
line M
2
line N
3
line O
pasteem seguida, cola os :caracteres nos caracteres, e toda a saída se parece com:
line A
line B
1:line X
2:line Y
3:line Z
line C
line D
1:line M
2:line N
3:line O
Estes são apenas exemplos - tudo poderia ser feito nos blocos de teste ou de execução aqui, mas o primeiro utilitário não deve consumir muita entrada.
Todos os utilitários envolvidos leram a mesma entrada - e imprimiram seus resultados - cada um por sua vez. Esse tipo de coisa pode ser difícil de pegar o jeito - porque diferentes utilitários vai tamponar mais do que outros - mas você pode geralmente dependem de dd, heade sedpara fazer a coisa certa (embora, para o GNU sed, você precisa do interruptor cli) e você sempre deve poder confiar read- porque é, por natureza, muito lento . E é por isso que o loop acima chama apenas uma vez por bloco de entrada.
nlnão precisa acumular estado . Vejanl -de verifique suasman/infopáginas para obter informações sobrenlo delimitador de seção .