dividere il file in due parti, secondo uno schema


14

Come dividere un file di grandi dimensioni in due parti, in un modello?

Di seguito un esempio file.txt:

ABC
EFG
XYZ
HIJ
KNL

Voglio dividere questo file in modo XYZtale che file1contenga le righe fino XYZe il resto delle righe file2.


La XYZlinea dovrebbe essere inclusa nell'output o no?
terdon

@terdon Nel mio caso nessuna riga "XYZ" non dovrebbe far parte di file2. Ma se hai un modo per farlo, aggiungi per rispondere. Potrebbe essere utile in alcuni altri casi.
d.putto

Abbastanza giusto, fatto.
terdon

Risposte:


10

Con awkte puoi fare:

awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile


Spiegazione: Il primo awkargomento ( out=file1) definisce una variabile con il nome file che verrà utilizzata per l'output mentre largefileviene elaborato l'argomento successivo ( ). Il awkprogramma stamperà tutte le righe sul file specificato dalla variabile out( {print >out}). Se XYZverrà trovato il modello , la variabile di output verrà ridefinita in modo che punti al nuovo file ( {out="file2}") che verrà utilizzato come destinazione per stampare le successive righe di dati.

Riferimenti:


14

Questo è un lavoro per csplit:

csplit -sf file -n 1 large_file /XYZ/

sdividerebbe il file in modo non intenzionale, creando pezzi con pre fix filee nombreggiati usando una sola cifra, ad es file0. ecc. Si noti che l'uso /regex/si dividerebbe, ma non includendo la linea corrispondente regex. Per dividere e includere la corrispondenza della linea regexaggiungere un +1offset:

csplit -sf file -n 1 large_file /XYZ/+1

Questo crea due file file0e file1. Se hai assolutamente bisogno che vengano nominati file1e file2puoi sempre aggiungere un modello vuoto al csplitcomando e rimuovere il primo file:

csplit -sf file -n 1 large_file // /XYZ/+1

crea file0, file1e file2ma file0è vuota, quindi si può tranquillamente rimuovere:

rm -f file0

Questa, penso, è la risposta più semplice. Tutto quello che devi fare è elencare alcuni schemi e il file verrà diviso per ordine. Brillante!
Henry Blyth,

6

Con un moderno kshecco una variante di shell (cioè senza sed) di una delle sedrisposte basate sopra:

{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1


E un'altra variante in kshsolo (cioè omettendo anche il cat):

{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1


(La kshsoluzione pura sembra essere abbastanza performante; su un file di test da 2,4 GB ha richiesto 19-21 secondi, rispetto ai 39-47 secondi con l' approccio sed/ catbased).


È molto veloce. Ma non penso che tu debba reade print- dovresti lasciarlo andare per produrre tutto da solo. Le prestazioni migliorano se costruisci interamente il toolkit AST e kshcompili tutti i builtin - è strano per me che sednon sia uno di loro, in realtà. Ma con cose come while <file doimmagino non ti serva sedmolto ...
Mikeserv,

Sono curioso però: come si sono awkcomportati nel tuo benchmark? E mentre sono abbastanza sicuro kshche probabilmente vincerai sempre questa lotta, se stai usando una GNU sednon sei molto onesto sed- GNU -uè un approccio pessimo per garantire POSIXLY l'offset del descrittore quando il programma si chiude esso - non dovrebbe essere necessario rallentare il normale funzionamento del programma - il buffering va bene - tutto ciò che seddovrebbe fare è cercare il descrittore al termine. Per qualsiasi motivo GNU inverte questa mentalità.
Mikeserv,

@mikeserv; La corrispondenza del modello di reindirizzamento viene eseguita fino a quando non viene trovato il modello e la linea con il modello trovato non verrà stampata se non esplicitamente eseguita come illustrato. (Almeno questo ha mostrato il mio test.) Nota che non c'è while; la stampa viene implicitamente eseguita come effetto collaterale definito <##dell'operatore di reindirizzamento. E solo la riga corrispondente deve essere stampata. (In questo modo l'implementazione delle funzionalità della shell è più flessibile per il supporto di incl./excl.) Un whileciclo esplicito che mi aspetto sia significativamente più lento (ma non ho verificato).
Janis,

1
@mikeserv; Ah ok. A proposito, ho appena provato il headinvece del read; sembra solo un po 'più lento, ma è il codice terser: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Janis,

1
@mikeserv; Buon punto; non lo era. Ma quando attivo il builtin (appena fatto e verificato i risultati) sono gli stessi numeri, stranamente. (Forse qualche funzione di chiamata in testa rispetto a leggere?)
Janis,

6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

Con GNU seddovresti usare l' -uinterruttore nbuffered. La maggior parte degli altri seddovrebbe funzionare.

Per lasciare fuori XYZ ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1

3

Prova questo con GNU sed:

sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file

sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
Breve

1

Un trucco semplice è stampare su STDOUT o STDERR, a seconda che il modello target sia stato abbinato. È quindi possibile utilizzare gli operatori di reindirizzamento della shell per reindirizzare l'output di conseguenza. Ad esempio, in Perl, supponendo che venga chiamato il file di input, fi due file di output f1e f2:

  1. Scartando la linea che corrisponde al modello di divisione:

    perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
  2. Inclusa la linea abbinata:

    perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2

In alternativa, stampa su diversi handle di file:

  1. Scartando la linea che corrisponde al modello di divisione:

    perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
    if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
  2. Inclusa la linea abbinata:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
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.