Cambia l'ordine delle linee in un file


11

Sto cercando di cambiare l'ordine delle linee in uno schema specifico. Lavorare con un file con molte righe (es. 99 righe). Per ogni tre righe, vorrei che la seconda riga fosse la terza riga e che la terza fosse la seconda riga.

ESEMPIO.

1- Ingresso:

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

2- Uscita:

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

Risposte:


12

Utilizzo awke numeri interi:

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

L'operatore del modulo esegue la divisione dei numeri interi e restituisce il resto, quindi per ogni riga restituirà la sequenza 1, 2, 0, 1, 2, 0 [...]. Sapendo ciò, salviamo semplicemente l'input su righe in cui il modulo è 2 per dopo - vale a dire, subito dopo aver stampato l'input quando è zero.


Abbiamo un piccolo difetto qui. Vedi la mia risposta, piccola parte di miglioramento
Sergiy Kolodyazhnyy,

Grazie per la buona cattura; Ho incorporato una correzione nella mia risposta sotto forma di 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

Cioè, pstampa la linea corrente, prendi quella next, hvecchia, prendi quella next, Ge la linea trattenuta (aggiungila allo spazio del pattern) e pstampa lo spazio del pattern a 2 righe con la terza e la seconda riga scambiate.


3

Un altro approccio awk :

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

Il risultato:

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- estrae i successivi 2 record se esistono

  • ogni 2o e 3o record sono assegnati L2e L3variabili rispettivamente


1
Suppongo che queste variabili inizino con la lettera L (lettere minuscole). Sono scelte scarse per la leggibilità perché sembrano i numeri per dodici e tredici. Una scelta migliore potrebbe essere line2, ecc.
In pausa fino a ulteriore avviso.

@DennisWilliamson, modificato in maiuscolo
RomanPerekhrest il

1

Utilizzando perle un breve script:

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.

Lo script elabora l'intero file, per ogni riga (memorizzata $_) otterrà le due righe successive ( $l2e $l3) e le stamperà nell'ordine richiesto: riga1, riga3, riga2.


1

Un modo potrebbe essere il seguente:

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

In alternativa,

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

risultati

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

1

Perché non fare solo un ciclo while? In forma estesa:

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

In "formato a linea singola":

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

Uscite:

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

1

Perl

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

L'idea qui è che usiamo l'operatore modulo %con la $.variabile del numero di riga , per capire quale è ogni primo, quale è ogni secondo e quale è ogni 3a riga. Per ogni 3a riga il resto è 0, mentre per ogni 1a e 2a riga avrà numeri corrispondenti.

Test:

$ 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.

Piccolo miglioramento

L'approccio con la memorizzazione della seconda riga in una variabile ha un difetto. Cosa succede se l'ultima riga è la "seconda", ovvero per quel resto di numero di riga è 2? Il codice originale nella risposta my e DopeGhoti non verrà stampato My dog is orangese tralasciamo l'ultima riga. La soluzione per questo in entrambi i casi è l'uso del END{}blocco di codice, con la disattivazione della variabile temporanea dopo la stampa. In altre parole:

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

e

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

In questo modo, il codice funzionerà per un numero arbitrario di righe in un file, non solo per quelle divisibili per 3.

Correzione aggiuntiva per il problema menzionato nei commenti

Nel caso di awk, se l'ultima riga nel file produce un output di 1 per $. % 3, il codice precedente ha emesso un problema di emissione di una nuova riga vuota a causa della stampa incondizionata di END{print delay}, poiché la printfunzione menzionata nei commenti aggiunge sempre una nuova riga a qualunque variabile su cui sta operando. In caso di perlversione questo problema non si verifica, poiché con la funzione -neflags printnon si aggiunge la nuova riga.

Tuttavia, la soluzione nel caso di awk è quella di rendere condizionale, come indicato da Dope Ghoti nei commenti, è verificare la lunghezza della variabile temporanea. La versione perl della stessa correzione sarebbe:

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

1
La tua correzione ha un suo potenziale piccolo difetto in quanto aggiungerà una riga vuota di output per i file con il numero "errato" di righe. Ho risolto questo problema nella mia integrazione del tuo miglioramento nella mia risposta con (per awk) NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti,

1
@DopeGhoti Il problema non si verifica con perl, poiché la stampa di perl con -neflag non genera una nuova riga. In effetti stampa, ma è una stringa nulla, nessuna nuova riga finale. Tuttavia, ho aggiunto la menzione del problema e la stessa correzione nella mia risposta. Grazie !
Sergiy Kolodyazhnyy,

1

Vim

Non adatto per file lunghi, ma comunque utile se stavi solo modificando un file e volessi, ad esempio, riordinare alcune stanze yaml.

Prima registra una macro:

gg qq j ddp j q

E quindi ripetere il numero desiderato di volte:

@q @q @q ...

O solo ad es

3@q

Spiegazione:

  • gg - vai alla prima riga
  • qq - avvia la registrazione di una macro
  • j - vai alla seconda riga
  • ddp - scambia la seconda e la terza riga
  • j - vai alla quarta riga, ovvero alla prima delle tre righe successive
  • q - interrompe la registrazione
  • @q - riproduce una volta la macro
  • 3 @ q - riproduce la macro tre volte

1
Invece la ripetizione manuale @q @q @q, è possibile farlo in questo modo 3@q: ripetere tre volte. 100@q- ripetere la macro 100 volte.
MiniMax

0

Uso: ./shuffle_lines.awk input.txt

Controlla shebang #!/usr/bin/awk -f, perché la awkposizione potrebbe differire sul tuo sistema.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.