A menos que seus segmentos sejam realmente grandes (como em: você realmente não pode poupar tanta memória RAM, provavelmente porque este é um pequeno sistema incorporado que controla um grande sistema de arquivos), uma única passagem é realmente a melhor abordagem. Não apenas porque será mais rápido, mas o mais importante, porque permite que a fonte seja um fluxo, a partir do qual os dados lidos e não salvos são perdidos. Este é realmente um trabalho para o awk, embora o sed também possa fazê-lo.
sed -n -e 's/^---$//' -e 't a' \
-e 'H' -e '$g' -e '$s/^\n//' -e '$p' -e 'b' \
-e ':a' -e 'h' # you are not expected to understand this
awk '{if (/^---$/) {chunk=""} # separator ==> start new chunk
else {chunk=chunk $0 RS}} # append line to chunk
END {printf "%s", chunk}' # print last chunk (without adding a newline)
Se você precisar usar uma abordagem de duas passagens, determine o deslocamento da linha do último separador e imprima a partir dele. Ou determine o deslocamento de bytes e imprima a partir dele.
</input/file tail -n +$((1 + $(</input/file # print the last N lines, where N=…
grep -n -e '---' | # list separator line numbers
tail -n 1 | # take the last one
cut -d ':' -f 1) )) # retain only line number
</input/file tail -n +$(</input/file awk '/^---$/ {n=NR+1} END {print n}')
</input/file tail -c +$(</input/file LC_CTYPE=C awk '
{pos+=length($0 RS)} # pos contains the current byte offset in the file
/^---$/ {last=pos} # last contains the byte offset after the last separator
END {print last+1} # print characters from last (+1 because tail counts from 1)
')
Adendo: Se você possui mais do que o POSIX, aqui está uma versão simples de uma passagem que se baseia em uma extensão comum do awk que permite que o separador de registros RS
seja uma expressão regular (o POSIX permite apenas um único caractere). Não está completamente correto: se o arquivo terminar com um separador de registros, ele imprimirá o pedaço antes do último separador de registros, em vez de um registro vazio. A segunda versão RT
evita esse defeito, mas RT
é específica do GNU awk.
awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'
Gilles 'SO- parar de ser mau'
fonte
sed
está funcionando bem, mas não consigoawk
executar o exemplo; trava ... e recebo um erro no terceiro exemplo:cut -f ':' -t 1
... cut: invalid option - 't'cut
exemplo. Não vejo nada de errado com oawk
exemplo, qual versão do awk você está usando e qual é a sua entrada de teste?awk
versão está funcionando .. está demorando muito tempo em um arquivo grande .. ased
versão processou o mesmo arquivo em 0.470s .. Meus dados de teste são muito ponderados ... apenas dois pedaços com um único '---' três linhas a partir do final de 1 milhão de linhas ...Uma estratégia de dois passes parece ser a coisa certa. Em vez de sed, eu usaria
awk(1)
. Os dois passes podem ficar assim:para obter o número da linha. E, em seguida, faça eco de todo o texto a partir desse número de linha com:
Isso não deve exigir armazenamento em buffer excessivo.
fonte
awk -v line=$(awk '/^---$/{n=NR}END{print n}' file) 'NR>line' file
O primeiro
sed
gera números de linha das linhas "---" ...O segundo
sed
extrai o último número da saída do primeiro sed ...Adicione 1 a esse número para iniciar o bloco "ccc" ...
O terceiro saídas 'sed' desde o início do bloco "ccc" para o EOF
Atualização (com informações recomendadas sobre os métodos Gilles)
Bem, eu estava pensando sobre o desempenho de Glenn Jackman
tac
, então testei as três respostas (no momento da redação) ... Os arquivos de teste continham 1 milhão de linhas (de seus próprios números de linhas).Todas as respostas fizeram o que era esperado ...
Aqui estão os tempos ..
Gilles
sed
(passe único)Gilles
awk
(passe único)Gilles 'two-pass' (primeiro método)
Gilles 'two-pass' (segundo método) ... muito rápido
Gilles 'two-pass' (terceiro método)
Gilles 'gawk' (método RT) ... muito rápido , mas não é POSIX.
glenn jackman ... muito rápido , mas não é POSIX.
fred.bear
Mackie Messer
fonte
Use " tac ", que exibe as linhas de um arquivo do começo ao fim:
fonte
tac
não é POSIX, é específico do Linux (está no GNU coreutils e em algumas instalações do busybox).Você poderia apenas usar
ed
Como funciona:
t
duplica a.
linha atual ( ) - que é sempre a última linha quando éed
iniciada (apenas no caso de o delimitador estar presente na última linha),1,?===?d
exclui todas as linhas até e incluindo a correspondência anterior (ed
ainda está na última linha) )$d
exclui a última linha (duplicada),,p
imprime o buffer de texto (substitua porw
para editar o arquivo no local) e finalmenteq
fechaed
.Se você sabe que há pelo menos um delimitador na entrada (e não se importa se também é impresso),
seria o mais curto.
Como funciona: anexa todas as linhas ao
H
buffer antigo, substitui oh
buffer antigo ao encontrar uma correspondência,d
exclui todas as linhas, exceto a última$
quandox
altera os buffers (e as impressões automáticas).fonte