podziel plik na dwie części, według wzoru

14

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 XYZtak, aby file1zawierał wiersze w górę XYZi resztę wierszy file2.

d.putto
źródło
Czy XYZwiersz powinien być zawarty w wyniku, czy nie?
terdon
@terdon W moim przypadku żadna linia „XYZ” nie powinna być częścią pliku2. Ale jeśli masz na to sposób, dodaj odpowiedź. Może być przydatny w niektórych innych przypadkach.
d.putto
W porządku, gotowe.
terdon

Odpowiedzi:

10

Dzięki awkniemu możesz:

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


Objaśnienie: Pierwszy awkargument ( out=file1) definiuje zmienną z nazwą pliku, która będzie używana do wyjścia, podczas largefileprzetwarzania kolejnego argumentu ( ). awkProgram drukuje wszystkie linie do pliku określonego przez zmienną out( {print >out}). Jeśli wzorzec XYZzostanie 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:

Janis
źródło
14

To jest praca dla csplit:

csplit -sf file -n 1 large_file /XYZ/

byłoby silently podzielić plik, tworząc kawałki z pre fIX filei numbered użyciu pojedynczej cyfry, np file0itd. Zauważ, że używanie /regex/byłoby rozdzielić, ale nie w tym wierszu, który pasuje regex. Aby podzielić i uwzględnić dopasowanie linii, regexdodaj +1przesunięcie:

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

Spowoduje to utworzenie dwóch plików file0i file1. Jeśli absolutnie potrzebujesz ich nazwy file1i file2zawsze możesz dodać pusty wzór do csplitpolecenia i usunąć pierwszy plik:

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

tworzy file0, file1a file2ale file0jest pusty, dzięki czemu można bezpiecznie go usunąć:

rm -f file0
don_crissti
źródło
Myślę, że to najprostsza odpowiedź. Wszystko, co musisz zrobić, to wymienić kilka wzorców, a plik zostanie przez nie podzielony w kolejności. Znakomity!
Henry Blyth,
6

Z nowoczesnym kshoto wariant powłoki (tj. Bez sed) jednej z sedpowyższych odpowiedzi na podstawie:

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


I inny wariant kshsam (tj. Również z pominięciem cat):

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


(Czyste kshrozwią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).

Janis
źródło
Jest bardzo szybki. Ale nie sądzę, że musisz readi printpowinieneś po prostu pozwolić mu odejść i wydać własne. Wydajność staje się lepsza, jeśli zbudujesz kompletny zestaw narzędzi AST i kshskompilujesz wszystkie wbudowane komponenty - to dziwne dla mnie, że sedtak naprawdę nie jest jednym z nich. Ale przy takich rzeczach, jak while <file dosądzę, nie potrzebujesz sedtak dużo ...
Mikeserv
Jestem jednak ciekawy - jak awkwypadł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 -unieprzyzwoite 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 sedmusisz zrobić, to poszukać deskryptora po zakończeniu. Z jakiegokolwiek powodu GNU odwraca tę mentalność.
mikeserv
@mikeserv; Dopasowanie wzorca przekierowania jest wykonywane do momentu znalezienia wzorca, a linia ze znalezionym wzorcem nie zostanie wydrukowana, jeśli nie zostanie wyraźnie wykonana zgodnie z rysunkiem. (Przynajmniej to pokazało mój test.) Zauważ, że nie ma 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 whilepętla oczekiwałbym, że będzie znacznie wolniejsza (ale nie sprawdzona).
Janis,
1
@mikeserv; Ah, dobrze. BTW, właśnie spróbowałem headzamiast read; wydaje się tylko trochę wolniej, ale to jest kod terser: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Janis,
1
@mikeserv; Słuszna uwaga; nie było. Ale kiedy aktywuję wbudowane (właśnie zrobione i sprawdzone wyniki), to dziwnie te same liczby. (Może narzut wywołania funkcji w porównaniu do odczytu?)
Janis
6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

W GNU sedpowinieneś użyć -uprzełącznika nbuffered. Większość innych sedpowinna po prostu działać.

Aby pominąć XYZ ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1
mikeserv
źródło
3

Wypróbuj to z GNU sed:

sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file
Cyrus
źródło
Krótszy:sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
don_crissti
1

Ł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, fa dwa pliki wyjściowe f1i f2:

  1. 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
  2. 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:

  1. 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
    
  2. W tym dopasowana linia:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
    
terdon
źródło