ordem inversa de parágrafos no arquivo

8

Eu tenho um arquivo que contém texto em parágrafos (linhas com texto separadas por uma ou mais linhas vazias). Gostaria de reverter a ordem dos parágrafos (ou seja, o último parágrafo se tornará o primeiro, ...), de preferência usando sed.

Estou procurando um comando sed que faria com um arquivo de parágrafos, o tacque faria com um arquivo de linhas.

Martin Vegter
fonte

Respostas:

6

O uso sednão é tão simples como mencionado por Joseph R .. No entanto, você poderia dizer:

sed '/./{H;d;};x;s/\n/={NL}=/g' inputfile | \
sed -e 's/^={NL}=//' -e '1!G;h;$!d' | \
sed G | sed 's/={NL}=/\'$'\n/g'

Dada uma amostra de entrada:

Para 1 line 1
Para 1 line 2
Para 1 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 3 line 1
Para 3 line 2
Para 3 line 3

isso produziria:

Para 3 line 1
Para 3 line 2
Para 3 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 1 line 1
Para 1 line 2
Para 1 line 3

Vale ressaltar que esta solução (assim como a alternativa Perl) requerem uma linha em branco no final do arquivo de entrada para funcionar como esperado.

devnull
fonte
6

Esta solução usa ambos tace perlpara ler um parágrafo de cada vez. Não requer a leitura do arquivo inteiro na memória.

tac file | perl -00 -lpe '$_ = join "\n", reverse split /\n/'

Inverta todas as linhas do arquivo e, para cada parágrafo invertido, inverta as linhas.

Glenn Jackman
fonte
Isso parece muito elegante e eficiente. No entanto, esta solução também condensa múltiplos vazios (isto é, separadora) linhas em um
Martin Vegter
3

Pode haver uma maneira de fazer isso sed, mas duvido que seja simples. Aqui está como eu faria isso no Perl:

perl -n00e 'push @paragraphs,$_; END{print for reverse @paragraphs}' your_file

Isso funciona porque a definição do separador de registro de entrada como o caractere nulo ( -00) indica ao Perl para operar no modo de parágrafo. A definição de parágrafo 1 do Perl corresponde exatamente à sua definição.


1 Olhe sob o cabeçalhoOther values for $/

Joseph R.
fonte
isso funciona de fato. O único pequeno problema é que ele não preserva várias linhas vazias que separam os parágrafos. Em vez disso, todos os parágrafos são separados por exatamente uma linha vazia.
Martin Vegter
1

Se seus parágrafos são sempre separados por uma única linha vazia:

sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed 's/^\x03//;1s/\x03$//;1!G;h;$!d;$a\' | tr $'\003' \\n

É muito fácil ver como ele funciona se você quebrá-lo em pedaços e executar sed '/^$/s/^/\x02/' infile, em seguida, sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\ne assim por diante ...


Se seus parágrafos estiverem separados por uma ou mais linhas vazias, por exemplo,

Para 1 line 1
Para 1 line 2

Para 2 line 1


Para 3 line 1
Para 3 line 2

Para 4 line 1
Para 4 line 2



Para 5 line 1

e você deseja reverter a ordem dos parágrafos, mas preservar a ordem dos "blocos vazios", você pode ler o arquivo duas vezes:
1º: transformar parágrafos em linhas únicas (removendo os blocos vazios no meio) e invertê-los e
2º: virar os blocos vazios em linhas únicas, "indexando" o número de linhas vazias em cada bloco (e removendo as linhas não vazias),
depois pasteos resultados e processe a saída para restaurar novas linhas:

paste -d $'\004' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) \
| sed '$!s/\x04/\n/;$s/\x04$//' | tr $'\003\002' \\n\\n

quais saídas:

Para 5 line 1

Para 4 line 1
Para 4 line 2


Para 3 line 1
Para 3 line 2

Para 2 line 1



Para 1 line 1
Para 1 line 2

Se você não se importa com uma linha de fuga extra na saída, pode soltar a última sed:

paste -d $'\n' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) | \
tr $'\003\002' \\n\\n

Eles assumem que a primeira e a última linha não estão vazias (e não \x02, \x03ou \x04na entrada).

don_crissti
fonte
1

Você pode fazê-lo com uma única instância de sed; sem tubos necessários. Como sedapenas uma pessoa passa pelo documento e como a parte do arquivo necessária no início da saída está no final do arquivo, será necessário reter o arquivo inteiro na memória sed(no espaço de espera) - para que possa não escala bem. Mas responde exatamente à pergunta:

:getpara
   ${
      s/$/\
/
      G
      s/\n\n$//
      q
   }
   N
   /\n$/!bgetpara
G
h
$!d
s/\n\n$//
q

Se não houver uma nova linha à direita, isso ainda funcionará bem. Se houver uma única linha nova à direita, ela será suprimida na saída (ou seja, não haverá uma nova linha à esquerda na saída). Se houver (por exemplo) 5 novas linhas à direita na entrada, haverá quatro novas linhas à saída.

As lacunas entre parágrafos são preservadas.

O espaço em branco em uma linha vazia não é tratado como uma quebra de parágrafo, mas isso é um recurso, não um bug. :)

Você também pode fazer isso como o one-liner muito menos legível:

sed ':k;${;s/\(\(\n\).*\)$/\1\2/;G;s/\n\n$//;q;};N;/\n$/!bk;G;h;$!d;s/\n\n$//;q' inputfile

Embora isso funcione apenas com o GNU sed. (Observe o uso complicado das referências anteriores para executar s/$/\n/. Sem isso, não seria uma linha única, pois conteria uma nova barra invertida.)

Curinga
fonte
então você sorve o arquivo, certo? parece que você coloca tudo no espaço de espera. w / G;h. você pode mencionar algo sobre restrições de entrada ou similar.
mikeserv
Não testei o one-liner porque estou trabalhando no meu Mac e não tenho o GNU à sedmão, mas a versão do script definitivamente preserva as lacunas entre os parágrafos. Acabei de testar na sua entrada. Você testou a versão do script?
Curinga
@ MikeServ: Definitivamente verdade. (Será atualizado hoje à noite.) #
29515 Wildcard
0
gem install facets

ruby -r facets/string \
     -e 'puts $stdin.read.strip.shatter(/\n\n+/).reverse.join("")' < file

Isso deve preservar o espaçamento entre parágrafos (embora seja mais legível que sed:)). No entanto, você deve adiantar uma resposta impressionante.

Amadan
fonte