Jak podzielić duży plik na dwie części, według wzoru?
Podany przykład file.txt
:
ABC
EFG
XYZ
HIJ
KNL
Chcę podzielić ten plik XYZ
tak, aby file1
zawierał wiersze w górę XYZ
i resztę wierszy file2
.
Jak podzielić duży plik na dwie części, według wzoru?
Podany przykład file.txt
:
ABC
EFG
XYZ
HIJ
KNL
Chcę podzielić ten plik XYZ
tak, aby file1
zawierał wiersze w górę XYZ
i resztę wierszy file2
.
Odpowiedzi:
Dzięki awk
niemu możesz:
awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile
Objaśnienie: Pierwszy awk
argument ( out=file1
) definiuje zmienną z nazwą pliku, która będzie używana do wyjścia, podczas largefile
przetwarzania kolejnego argumentu ( ). awk
Program drukuje wszystkie linie do pliku określonego przez zmienną out
( {print >out}
). Jeśli wzorzec XYZ
zostanie znaleziony, zmienna wyjściowa zostanie ponownie zdefiniowana w celu wskazania nowego pliku ( {out="file2}"
), który zostanie użyty jako cel do wydrukowania kolejnych linii danych.
Bibliografia:
To jest praca dla csplit
:
csplit -sf file -n 1 large_file /XYZ/
byłoby s
ilently podzielić plik, tworząc kawałki z pre f
IX file
i n
umbered użyciu pojedynczej cyfry, np file0
itd. Zauważ, że używanie /regex/
byłoby rozdzielić, ale nie w tym wierszu, który pasuje regex
. Aby podzielić i uwzględnić dopasowanie linii, regex
dodaj +1
przesunięcie:
csplit -sf file -n 1 large_file /XYZ/+1
Spowoduje to utworzenie dwóch plików file0
i file1
. Jeśli absolutnie potrzebujesz ich nazwy file1
i file2
zawsze możesz dodać pusty wzór do csplit
polecenia i usunąć pierwszy plik:
csplit -sf file -n 1 large_file // /XYZ/+1
tworzy file0
, file1
a file2
ale file0
jest pusty, dzięki czemu można bezpiecznie go usunąć:
rm -f file0
Z nowoczesnym ksh
oto wariant powłoki (tj. Bez sed
) jednej z sed
powyższych odpowiedzi na podstawie:
{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1
I inny wariant ksh
sam (tj. Również z pominięciem cat
):
{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1
(Czyste ksh
rozwiązanie wydaje się być dość wydajne; w pliku testowym 2,4 GB potrzebowało 19-21 sekund, w porównaniu do 39-47 sekund z podejściem opartym na sed
/ cat
).
read
i print
powinieneś po prostu pozwolić mu odejść i wydać własne. Wydajność staje się lepsza, jeśli zbudujesz kompletny zestaw narzędzi AST i ksh
skompilujesz wszystkie wbudowane komponenty - to dziwne dla mnie, że sed
tak naprawdę nie jest jednym z nich. Ale przy takich rzeczach, jak while <file do
sądzę, nie potrzebujesz sed
tak dużo ...
awk
wypadła twoja analiza? I chociaż jestem prawie pewien, ksh
że prawdopodobnie zawsze wygra tę walkę, jeśli używasz GNU sed
, nie jesteś wobec tego zbyt uczciwy sed
- GNU jest -u
nieprzyzwoite podejście do POSIXLY zapewniania przesunięcia deskryptora w miejscu wyjścia programu to - nie powinno być potrzeby spowalniania normalnej pracy programu - buforowanie jest w porządku - wszystko, co sed
musisz zrobić, to poszukać deskryptora po zakończeniu. Z jakiegokolwiek powodu GNU odwraca tę mentalność.
while
; drukowanie jest domyślnie wykonywane jako zdefiniowany efekt uboczny <##
operatora przekierowania. I tylko pasująca linia wymaga wydrukowania. (W ten sposób implementacja funkcji powłoki jest najbardziej elastyczna dla obsługi włączania / wyłączania.) Wyraźna while
pętla oczekiwałbym, że będzie znacznie wolniejsza (ale nie sprawdzona).
head
zamiast read
; wydaje się tylko trochę wolniej, ale to jest kod terser: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3
.
Wypróbuj to z 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
Łatwym hackiem jest wydrukowanie do STDOUT lub STDERR, w zależności od tego, czy wzorzec docelowy został dopasowany. Następnie można użyć operatorów przekierowania powłoki, aby odpowiednio przekierować dane wyjściowe. Na przykład w Perlu, zakładając, że plik wejściowy jest wywoływany, f
a dwa pliki wyjściowe f1
i f2
:
Odrzucanie linii pasującej do wzorca podziału:
perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
W tym dopasowana linia:
perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
Alternatywnie, drukuj do różnych uchwytów plików:
Odrzucanie linii pasującej do wzorca podziału:
perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
W tym dopasowana linia:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZ
wiersz powinien być zawarty w wyniku, czy nie?