Pegue o seguinte arquivo:
$ cat f1
stu vwx yza
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu
pqr stu vwx
stu vwx yza
Para imprimir todas as linhas desde a primeira que contém abc
até a primeira que contém mno
com GNU sed
:
$ sed -n '/abc/,/mno/p' f1
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
Como eu poderia imprimir todas as linhas até a última contendo mno
, por exemplo, como eu poderia obter o seguinte resultado:
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu
Em outras palavras, existe uma maneira de tornar sed
a seleção de intervalo do GNU gananciosa?
Atualizar
Na minha configuração:
- Se
mno
estiver faltando, deve imprimir tudo até o final do arquivo. mno
não pode ocorrer antes do primeiroabc
.- Sempre há pelo menos um
abc
e nunca estãoabc
namno
mesma linha
EDIT
Acabei de adicionar uma stu vwx yza
linha fictícia no início, para que o arquivo não comece com uma linha incluindo abc
(para evitar soluções que começam na primeira linha - elas devem começar na primeira linha abc
)
Você poderia usar
awk
se for uma opção. Você pode marcar as linhas para o início e a parada do padrão e imprimir essas linhas em uma passagem do arquivo (envolve o armazenamento de linhas a partir da primeira linha contendoabc
até a última linha em um buffer)Observe que isso não funcionará, quando o padrão inicial não estiver presente, imprimindo apenas uma série de linhas em branco.
Algoritmo de trabalho:
Dois intervalos de endereços são usados.
O primeiro
/abc/,$!d;
remove tudo até a primeira correspondência de padrão.O segundo
0,/mno/b;
até uma correspondência com o padrão/mno/
, envia cada buffer de linha (espaço do padrão) para a saída ignorando o script restante, evitando assim a exclusão caso o padrão não seja encontrado no arquivo.O resto do script
:1;/mno/b;$d;N;b1
funciona em um loop. No buffer do editor, as linhas são anexadas até que ocorra uma correspondência de padrão. Se um/mno/
padrão for encontrado, todo o buffer será enviado para a saída, ignorando o restante do script. Se nenhuma correspondência ocorrer, o buffer será excluído na última linha.Outra
awk
solução com menos buffering:abc
(onde define um sinalizadorf
como 1) até e incluindo a primeira ocorrência demno
. Af==1
instrução fora dos blocos de regras instruiawk
a imprimir a linha atual desde quef
esteja definida como1
.mno
(ondef
agora tem o valor 2) é armazenado em um bufferbuf
, que é impresso e limpo na próxima ocorrência demno
. Para garantir que lidamos corretamente com situações em que o firstmno
ocorre antes do firstabc
, exigimos quef
seja definido pelo menos 1 antes de aplicar essa lógica.Portanto, armazenará no máximo o texto entre duas ocorrências de
mno
, ou a última ocorrência demno
e fim de arquivo (só que a última parte nunca será impressa).Se você deseja trocar velocidade com eficiência de memória, o método de duas passagens a seguir não dependerá de buffer:
Isso processará o arquivo duas vezes (portanto, é especificado duas vezes como argumento).
FNR
, o contador de linhas por arquivo é igual aNR
, o contador de linhas global, procuramos a primeira ocorrência deabc
e a última ocorrência demno
, e armazenamos seus números de linha emstart
eend
, respectivamente.FNR
contador esteja entre (e incluindo) ostart
finalend
(ou simplesmente se for maior/igual do questart
seend
não estiver definido).Eu não acho que o sed pode ser ganancioso, não. Uma possível solução alternativa, simples mas ineficiente, seria processar o arquivo duas vezes. Uma para obter o intervalo de linhas e outra para imprimir. Por exemplo:
Como alternativa, se você quiser lidar com casos em que um ou ambos os padrões estão ausentes:
Se
abc
não for encontrado, imprimirá a partir do início do arquivo. Semnc
não for encontrado, imprimirá doabc
(ou do início, seabc
não estiver lá) até o final. Se nenhum padrão for encontrado, é claro que não imprimirá nada.Colete linhas até que uma
mno
sequência as deixe prontas para impressão:/abc/,$!d
d
exclui tudo, exceto o intervalo da primeiraabc
linha até o final. Isso também lida com o caso quando não há nenhumabc
.:loop
./mno/{p;d;}
se houvermno
no espaço do padrão,p
rint e comece de novo.$d
se chegarmos à última linha semmno
,d
elege tudo no buffer. Infelizmente, isso significa que não há saída, se não houvermno
.N
linha ext e continue o loop.Você pode coletar todas as linhas, começando pela
abc
linha no espaço de espera e, em seguida, usar a natureza gananciosa de.*
para excluir tudo após o últimomno
:/abc/,$!d
éd
deletar tudo antes da primeiraabc
linha (ou o arquivo inteiro, se não houver nenhumaabc
linha)H;$!d
é o padrão clássico para coletar o arquivo inteiro no espaço de espera (observe que isso pode ser um problema para arquivos muito grandes)x
buffers em vez de usarg
para evitar copiar um buffer grandes/\n//
remove a nova linha errada no início, produzida acrescentando ao espaço de espera vazios/\(.*mno[^\n]*\n\).*/\1/
remove tudo após a últimamno
linha (ou imprime todo o arquivo restante, se não houvermno
linha, conforme solicitado). Observe que[^\n]
não é POSIX e funcionará apenas em algumas versões como GNUsed
.Usando Raku (anteriormente conhecido como Perl_6)
Entrada de amostra:
Saída de amostra:
Observe acima que a Saída de Amostra é um retorno das linhas de 2 a 10 quando alimentada a Entrada de Amostra de 11 linhas. Além disso, quando a entrada de amostra é truncada apenas para as linhas de 1 a 10 (ou seja, com
mno
na última linha), o código Raku acima ainda (corretamente) retorna as linhas de 2 a 10.Obrigado a @ImHere e @ChennyStar por me estimular nos comentários a apresentar uma solução Raku mais robusta.
https://raku.org
Usando
GNU sed
Nota: a primeira linha abc não tem mno conforme OP, então podemos explorar esse fato no código sed abaixo.Neste método, usamos o modo slurp
-z
para ler o arquivo completo no espaço de padrões. Em seguida, excluímos até antes da primeira linha contendo abc. Depois disso, alcance a última linha mno usando a ganância do regex.Ainda outra maneira é uma abordagem de duas passagens, onde registramos os números de linha da primeira linha abc e da última linha mno. Caso não haja mno presente, preenchemos $ em seu lugar. Então, usando esses dois números, construímos um comando sed begin,end
p
;endq
Podemos usar
perl
para slurp o arquivo e, em seguida, o arquivo inteiro é uma longa string que queimamos de ambas as extremidades e paramos quando nossas condições são atendidas.Aqui estão mais maneiras de usar o editor sed para obter a saída desejada.
Outro método em que armazenamos linhas em espera apenas até que /mno/ seja visto. Nesse ponto, viramos e imprimimos o que estava em espera.
Aqui está a maneira do Python usando o método groupby do módulo itertools versátil para fazer o trabalho.