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.
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.
Risposte:
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:
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
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).
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 ...
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à.
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).
headinvece del read; sembra solo un po 'più lento, ma è il codice terser: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
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:
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>f2Inclusa la linea abbinata:
perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2In alternativa, stampa su diversi handle di file:
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 "$_";' fInclusa la linea abbinata:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZlinea dovrebbe essere inclusa nell'output o no?