inverser l'ordre des paragraphes dans le fichier

8

J'ai un fichier contenant du texte dans les paragraphes (lignes avec du texte séparé par une ou plusieurs lignes vides). Je voudrais inverser l'ordre des paragraphes (c'est-à-dire que le dernier paragraphe deviendra le premier, ...), de préférence en utilisant sed.

Je recherche une commande sed qui ferait un fichier de paragraphes, que tacferait un fichier de lignes.

Martin Vegter
la source

Réponses:

6

L'utilisation sedn'est pas aussi simple que celle mentionnée par Joseph R .. Cependant, vous pourriez dire:

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'

Étant donné un exemple d'entrée:

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

cela produirait:

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

Il convient de mentionner que cette solution (ainsi que l'autre solution Perl) nécessite une ligne vierge à la fin du fichier d'entrée afin de fonctionner comme prévu.

devnull
la source
6

Cette solution utilise les deux tacet perlpour lire un paragraphe à la fois. Il ne nécessite pas de lire l'intégralité du fichier en mémoire.

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

Inversez toutes les lignes du fichier, puis pour chaque paragraphe inversé, inversez les lignes.

glenn jackman
la source
Cela semble très élégant et efficace. Cependant, cette solution condense également plusieurs lignes vides (c'est-à-dire séparant) en une seule
Martin Vegter
3

Il pourrait y avoir un moyen de le faire sed, mais je doute que ce soit simple. Voici comment je le ferais en Perl:

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

Cela fonctionne car la définition du séparateur d'enregistrements d'entrée comme le caractère nul ( -00) indique à Perl de fonctionner en mode paragraphe. La définition de Perl d'un paragraphe 1 correspond exactement à votre définition.


1 Regardez sous le titreOther values for $/

Joseph R.
la source
cela fonctionne en effet. Le seul petit problème est qu'il ne conserve pas plusieurs lignes vides séparant les paragraphes. Au lieu de cela, tous les paragraphes sont séparés par exactement une ligne vide.
Martin Vegter
1

Si vos paragraphes sont toujours séparés par une seule ligne vide:

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

Il est assez facile de voir comment cela fonctionne si vous le divisez en morceaux et exécutez sed '/^$/s/^/\x02/' infileensuite sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\net ainsi de suite ...


Si vos paragraphes sont séparés par une ou plusieurs lignes vides, par exemple

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

et vous souhaitez inverser l'ordre des paragraphes mais conserver l'ordre des "blocs vides", vous pouvez lire le fichier deux fois:
1er: transformer les paragraphes en lignes simples (en supprimant les blocs vides entre les deux) et les inverser et
2e: tourner les blocs vides en lignes simples, "indexant" le nombre de lignes vides dans chaque bloc (et supprimant les lignes non vides)
puis pasteles résultats et traitent la sortie pour restaurer les retours à la ligne:

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

qui génère:

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

Si cela ne vous dérange pas une ligne de fin supplémentaire dans la sortie, vous pouvez supprimer la dernière 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

Ceux-ci supposent que la première et la dernière ligne ne sont pas vides (et non \x02, \x03ou \x04dans l'entrée).

don_crissti
la source
1

Vous POUVEZ le faire avec une seule instance de sed; aucun tuyau nécessaire. Étant donné sedqu'un seul passage dans le document et que la partie du fichier requise au début de la sortie se trouve à la fin du fichier, il faudra conserver l'intégralité du fichier en mémoire à l'intérieur sed(dans l'espace d'attente). pas bien à l'échelle. Mais cela répond exactement à la question:

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

S'il n'y a pas de nouvelle ligne de fin, cela fonctionne toujours bien. S'il n'y a qu'une seule nouvelle ligne de fin, elle est supprimée dans la sortie (c'est-à-dire qu'il n'y aura pas de nouvelle ligne principale dans la sortie). S'il y a (par exemple) 5 sauts de ligne de fin dans l'entrée, il y aura 4 sauts de ligne de début dans la sortie.

Les écarts entre les paragraphes sont préservés.

Les espaces sur une ligne autrement vide ne sont PAS traités comme un saut de paragraphe, mais c'est une fonctionnalité, pas un bogue. :)

Vous pouvez également le faire en tant que doublure beaucoup moins lisible:

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

Bien que cela ne fonctionne qu'avec GNU sed. (Notez l'utilisation délicate des références arrières pour effectuer s/$/\n/. Sans cela, ce ne serait pas une ligne simple littérale car elle contiendrait une nouvelle barre oblique inversée.)

Caractère générique
la source
donc vous slurpez le fichier, non? on dirait que vous mettez le tout dans un espace de retenue. w / G;h. vous pourriez mentionner quelque chose sur les restrictions d'entrée ou similaire.
mikeserv
Je n'ai pas testé le one-liner parce que je travaille depuis mon Mac et que je n'ai pas GNU à sedportée de main, mais la version du script conserve définitivement les espaces entre les paragraphes. Je viens de le tester sur votre entrée. Avez-vous testé la version du script?
Wildcard
@mikeserv: Certainement vrai. (Mise à jour ce soir.)
Wildcard
0
gem install facets

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

Cela devrait préserver l'espacement de vos paragraphes (tout en étant plus lisible que sed:)).

Amadan
la source