Ändern Sie die Reihenfolge der Zeilen in einer Datei

11

Ich versuche, die Reihenfolge der Linien in einem bestimmten Muster zu ändern. Arbeiten mit einer Datei mit vielen Zeilen (z. B. 99 Zeilen). Für alle drei Zeilen möchte ich, dass die zweite Zeile die dritte Zeile und die dritte die zweite Zeile ist.

BEISPIEL.

1- Eingabe:

gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...

2- Ausgabe:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...
Annick Raymond
quelle

Antworten:

12

Verwenden awkund ganzzahlige Mathematik:

awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay } }' /path/to/input

Der Moduloperator führt eine Ganzzahldivision durch und gibt den Rest zurück, sodass für jede Zeile die Sequenz 1, 2, 0, 1, 2, 0 [...] zurückgegeben wird. Da wir das wissen, speichern wir die Eingabe nur in Zeilen, in denen der Modul für später 2 ist - also gleich nach dem Drucken der Eingabe, wenn sie Null ist.

DopeGhoti
quelle
Wir haben hier einen kleinen Fehler. Siehe meine Antwort, kleiner Verbesserungsteil
Sergiy Kolodyazhnyy
Danke für den guten Fang; Ich habe eine Korrektur in Form von in meine Antwort aufgenommen NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti
23
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

Das heißt, pdrucken Sie die aktuelle Zeile, holen Sie sich die next- Zeile, machen Sie hsie alt, holen Sie sich die next- GZeile und halten Sie die gehaltene Zeile (hängen Sie sie an den Musterraum an) und pdrucken Sie diesen zweizeiligen Musterraum, wobei die dritte und zweite Zeile vertauscht sind.

Stéphane Chazelas
quelle
3

Ein weiterer awk- Ansatz:

awk '{print $0; if ((getline L2)>0 && (getline L3)>0){ print L3 ORS L2 }}' file

Die Ausgabe:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

  • (getline L2)>0 && (getline L3)>0- extrahiert die nächsten 2 Datensätze, falls vorhanden

  • jeweils 2. und 3. Datensätze zugewiesen L2und L3Variablen bzw.

RomanPerekhrest
quelle
1
Ich gehe davon aus, dass diese Variablen mit dem Buchstaben L (Kleinbuchstaben) beginnen. Sie sind eine schlechte Wahl für die Lesbarkeit, da sie wie die Ziffern für zwölf und dreizehn aussehen. Eine bessere Wahl könnte sein line2, etc.
Bis auf weiteres angehalten.
@ TennisWilliamson, geändert in Großbuchstaben
RomanPerekhrest
1

Verwenden perlund ein kurzes Skript:

user@pc:~$ cat input.txt 
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

user@pc:~$ perl -ne '$l2=<>; $l3=<>; print $_,$l3,$l2;' input.txt 
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Das Skript verarbeitet die gesamte Datei. Für jede Zeile (gespeichert in $_) erhält es die nächsten beiden Zeilen ( $l2und $l3) und druckt sie in der gewünschten Reihenfolge: Zeile1, Zeile3, Zeile2.

Frank Förster
quelle
1

Ein Weg könnte wie folgt sein:

sed -e '
   /\n/s/\(.*\)\(\n\)\(.*\)/\3\2\1/;//b
   $!N;$q;N;                            # load up the pattern space with 3 lines provided eof not reached
   P;D;                                 # first just print the first line then interchange the two and print them
' yourfile

Alternative,

perl -ne 'print $_, reverse scalar <>, scalar <>' yourfile

Ergebnisse

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

quelle
1

Warum nicht einfach eine while-Schleife machen? In erweiterter Form:

( while read a
  do
    read b
    read c
    echo "$a"
    echo "$c"
    echo "$b"
  done
) < input.txt

Im "einzeiligen Format":

( while read a ; do read b ; read c ; echo "$a" ; echo "$c" ; echo "$b" ; done) < input.txt

Ausgänge:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
Stephen Quan
quelle
1

Perl

perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt

Die Idee hier ist, dass wir den Modulo-Operator %mit der Zeilennummernvariablen verwenden $., um herauszufinden, welche jede erste, welche jede Sekunde und welche jede dritte Zeile ist. Für jede 3. Zeile ist der Rest 0, während für jede 1. und 2. Zeile entsprechende Nummern vorhanden sind.

Prüfung:

$ cat input.txt                                                                                                          
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt                                    
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Kleinere Verbesserung

Der Ansatz mit dem Speichern der zweiten Zeile in einer Variablen weist einen Fehler auf. Was ist, wenn die letzte Zeile die "zweite" ist, dh für diese Zeile ist der Rest 2? Der Originalcode in meiner und DopeGhotis Antwort wird nicht gedruckt, My dog is orangewenn wir die letzte Zeile weglassen . In beiden Fällen besteht die Lösung darin, einen END{}Codeblock zu verwenden und die temporäre Variable nach dem Drucken zu deaktivieren. Mit anderen Worten:

$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt

und

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt 

Auf diese Weise funktioniert der Code für eine beliebige Anzahl von Zeilen in einer Datei, nicht nur für die durch 3 teilbaren.

Zusätzlicher Fix für das in den Kommentaren erwähnte Problem

In awks Fall, wenn die letzte Zeile in der Datei eine Ausgabe von 1 für $ ergibt. % 3, der vorherige Code hat das Problem der Ausgabe einer leeren neuen Zeile aufgrund des bedingungslosen Druckens von END{print delay}, da printdie in den Kommentaren erwähnte Funktion immer eine neue Zeile an die Variable anfügt, mit der er arbeitet. Im Falle einer perlVersion tritt dieses Problem nicht auf, da mit der Funktion -neflags printdie Newline nicht angehängt wird.

Nichtsdestotrotz besteht die Lösung in awks Fall darin, bedingt zu machen, wie von Dope Ghoti in den Kommentaren erwähnt, die Länge der temporären Variablen zu überprüfen. Die Perl-Version des gleichen Fixes wäre:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 
Sergiy Kolodyazhnyy
quelle
1
Ihr Fix weist einen potenziellen kleinen eigenen Fehler auf, da eine leere Ausgabezeile für Dateien mit der 'falschen' Zeilenanzahl angehängt wird. Ich habe dies in meiner Einbeziehung Ihrer Verbesserung in meine Antwort mit (für awk) behoben NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti
1
@DopeGhoti Das Problem tritt bei Perl nicht auf, da der Druck von Perl mit -neFlags keine neue Zeile ausgibt. Es wird zwar gedruckt, aber es ist eine Nullzeichenfolge, keine nachgestellte Zeilenumbruch. Trotzdem habe ich die Erwähnung des Problems und den gleichen Fix in meine Antwort aufgenommen. Vielen Dank !
Sergiy Kolodyazhnyy
1

Vim

Nicht für lange Dateien geeignet, aber dennoch praktisch, wenn Sie nur eine Datei bearbeiten und beispielsweise einige Yaml-Strophen neu anordnen möchten.

Nehmen Sie zuerst ein Makro auf:

gg qq j ddp j q

Und dann die gewünschte Anzahl wiederholen:

@q @q @q ...

Oder einfach nur zB

3@q

Erläuterung:

  • gg - gehe zur ersten Zeile
  • qq - Starten Sie die Aufnahme eines Makros
  • j - gehe zur zweiten Zeile
  • ddp - vertausche die zweite und die dritte Zeile
  • j - gehe zur vierten Zeile, dh zur ersten der nächsten drei Zeilen
  • q - Aufnahme beenden
  • @q - Wiederhole das Makro einmal
  • 3 @ q - Wiederholen Sie das Makro dreimal
Edheldil
quelle
1
Statt manuell zu wiederholen @q @q @q, ist dies auf diese Weise möglich 3@q- dreimal wiederholen. 100@q- Wiederholen Sie das Makro 100 Mal.
MiniMax
0

Verwendung: ./shuffle_lines.awk input.txt

Überprüfen Sie shebang #!/usr/bin/awk -f, da der awkStandort auf Ihrem System unterschiedlich sein kann.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
MiniMax
quelle