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 XYZ
tale che file1
contenga le righe fino XYZ
e 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 XYZ
tale che file1
contenga le righe fino XYZ
e il resto delle righe file2
.
Risposte:
Con awk
te puoi fare:
awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile
Spiegazione: Il primo awk
argomento ( out=file1
) definisce una variabile con il nome file che verrà utilizzata per l'output mentre largefile
viene elaborato l'argomento successivo ( ). Il awk
programma stamperà tutte le righe sul file specificato dalla variabile out
( {print >out}
). Se XYZ
verrà 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/
s
dividerebbe il file in modo non intenzionale, creando pezzi con pre f
ix file
e n
ombreggiati 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 regex
aggiungere un +1
offset:
csplit -sf file -n 1 large_file /XYZ/+1
Questo crea due file file0
e file1
. Se hai assolutamente bisogno che vengano nominati file1
e file2
puoi sempre aggiungere un modello vuoto al csplit
comando e rimuovere il primo file:
csplit -sf file -n 1 large_file // /XYZ/+1
crea file0
, file1
e file2
ma file0
è vuota, quindi si può tranquillamente rimuovere:
rm -f file0
Con un moderno ksh
ecco una variante di shell (cioè senza sed
) di una delle sed
risposte basate sopra:
{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1
E un'altra variante in ksh
solo (cioè omettendo anche il cat
):
{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1
(La ksh
soluzione 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
/ cat
based).
read
e print
- dovresti lasciarlo andare per produrre tutto da solo. Le prestazioni migliorano se costruisci interamente il toolkit AST e ksh
compili tutti i builtin - è strano per me che sed
non sia uno di loro, in realtà. Ma con cose come while <file do
immagino non ti serva sed
molto ...
awk
comportati nel tuo benchmark? E mentre sono abbastanza sicuro ksh
che probabilmente vincerai sempre questa lotta, se stai usando una GNU sed
non 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 sed
dovrebbe 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 while
ciclo esplicito che mi aspetto sia significativamente più lento (ma non ho verificato).
head
invece 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, f
i due file di output f1
e 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>f2
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:
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
Inclusa la linea abbinata:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZ
linea dovrebbe essere inclusa nell'output o no?