2011-07-25 13 views
12

Ho fatto questo a mano e non ce la faccio più. Ho migliaia di righe e penso che questo sia un lavoro per sed o awk.Come passare/ruotare ogni due righe con sed/awk?

In sostanza, abbiamo un file in questo modo:

A sentence X 
A matching sentence Y 
A sentence Z 
A matching sentence N 

Questo ciclo continua per l'intero file. Voglio capovolgere ogni frase e frase corrispondente in modo che l'intero file finirà come:

A matching sentence Y 
A sentence X 
A matching sentence N 
A sentence Z 

Qualche consiglio?

edit: l'estensione del problema iniziale

Dimitre Radoulov fornito una grande risposta per il problema iniziale. Questa è un'estensione del problema principale: alcuni ulteriori dettagli:

Diciamo che abbiamo un file organizzato (a causa della linea di sed Dimitre dato, il file è organizzato). Tuttavia, ora desidero organizzare il file in ordine alfabetico, ma solo utilizzando la lingua (inglese) della seconda riga.

watashi 
me 
annyonghaseyo 
hello 
dobroye utro! 
Good morning! 

Mi piacerebbe organizzare in ordine alfabetico tramite le frasi inglesi (ogni 2a frase). Dato l'ingresso sopra, questo dovrebbe essere l'uscita:

dobroye utro! 
Good morning! 
annyonghaseyo 
hello 
watashi 
me 

risposta

8
sed 'N; 
s/\(.*\)\n\(.*\)/\2\ 
\1/' infile 

N - accodare la successiva linea di ingresso nello spazio modello
\(.*\)\n\(.*\) - salvare le parti corrispondenti del pattern space quella prima e quello dopo il newline.
\2\\ \1 - scambiare le due linee (\ 1 è la prima parte salvata, \ 2 il secondo). Usa la nuova riga letterale di escape per la portabilità

Con alcune implementazioni di sed, è possibile utilizzare la sequenza di escape \ n: \2\n\1.

+0

Grazie-- questo ha funzionato come l'oro! Sarebbe possibile riorganizzare alfabeticamente in base alla prima lettera della prima riga? Inoltre, sembra che le dimensioni del file siano aumentate di circa il 30% dopo questo, possibilmente inserito alcuni simboli? Non vedo nessuno spazio bianco ecc. Rimuovo tutto lo spazio bianco finale con ":% s/\ s \ + $ //" in vim. modifica: ho salvato l'output tramite> output.txt se questo è importante. –

+0

@Google, potresti pubblicare un campione più grande del tuo input e un esempio dell'output desiderato (dato l'ultimo requisito di ordinazione)? –

+0

Ho aggiornato il problema iniziale, spero sia chiaro. Se no fammelo sapere –

2

Ipotizzando un file di input in questo modo:

A sentence X 
Z matching sentence Y 
A sentence Z 
B matching sentence N 
A sentence Z 
M matching sentence N 

Si potrebbe fare entrambe le cose lo scambio e ordinare con Perl:

perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort keys %_; 
    }' infile 

L'output che ottengo è:

% perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort keys %_; 
    }' infile 
B matching sentence N 
A sentence Z 
M matching sentence N 
A sentence Z 
Z matching sentence Y 
A sentence X 

Se si desidera ordinare dalla prima riga (prima dello scambio):

perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort { 
     $_{ $a } cmp $_{ $b } 
     } keys %_; 
    }' infile 

Quindi, se il file originale è simile al seguente:

% cat infile1 
me 
watashi 
hello 
annyonghaseyo 
Good morning! 
dobroye utro! 

L'output dovrebbe essere simile a questo:

% perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort { 
    $_{ $a } cmp $_{ $b } 
    } keys %_; 
    }' infile1 
dobroye utro! 
Good morning! 
annyonghaseyo 
hello 
watashi 
me 

Questa versione dovrebbe gestire i record duplicati in modo corretto:

perl -lne' 
$_{ $_, $. } = $v unless $. % 2; 
$v = $_; 
END { 
    print substr($_, 0, length() - 1) , $/, $_{ $_ } 
    for sort { 
     $_{ $a } cmp $_{ $b } 
     } keys %_; 
    }' infile 

E un'altra versione, inspi rosso dalla soluzione postato da Glenn (scambio di registrazione incluso e supponendo che il _ZZ_ modello non è presente nel file di testo):

sed 'N; 
    s/\(.*\)\n\(.*\)/\1_ZZ_\2/' infile | 
    sort | 
     sed 's/\(.*\)_ZZ_\(.*\)/\2\ 
\1/' 
+0

Wow, grazie! Funziona alla perfezione-- L'ho avvolto in uno script bash. Mi hai risparmiato un sacco di lavoro. Molte grazie! –

+0

Dopo un'attenta ispezione sembra che funzioni bene, ma non gestisce i duplicati. C'è un modo per gestire con grazia? Sembra che lasci dei duplicati. –

+0

@Google, hai ragione. Aggiunta una versione fissa. –

6

Prima domanda:

awk '{x = $0; getline; print; print x}' filename 

domanda successiva: ordina per 2 ° linea

paste - - < filename | sort -f -t $'\t' -k 2 | tr '\t' '\n' 

cui uscite:

dobroye utro! 
Good morning! 
annyonghaseyo 
hello 
watashi 
me 
17

Per la prima parte della questione, ecco un modo per scambiare ogni altra linea con l'altro in sed senza usare le espressioni regolari:

sed -n 'h;n;p;g;p' 

La riga di comando -n sopprime la stampa automatica. Il comando h inserisce le copie della riga corrente dallo spazio del modello nello spazio di attesa, n legge nella riga successiva allo spazio del motivo e p lo stampa; g copia la prima riga dallo spazio di attesa nello spazio pattern, riportando la prima riga nello spazio pattern e p lo stampa.

+1

Questo è molto bello! –

+0

soluzione fantastica! – hovanessyan

+3

Se l'input ha un numero dispari di righe, la linea finale non verrà emessa. Se l'output di quella linea è desiderato: 'sed -n '$ p; h; n; p; g; p'' –

Problemi correlati