разбить файл на две части по шаблону

14

Как разбить большой файл на две части, по шаблону?

Приведенный пример file.txt:

ABC
EFG
XYZ
HIJ
KNL

Я хочу разделить этот файл на XYZтакой, который file1содержит строки до XYZи остальные строки в file2.

d.putto
источник
Должна ли XYZлиния быть включена в вывод или нет?
Terdon
@terdon В моем случае никакая строка "XYZ" не должна быть частью file2. Но если у вас есть способ сделать это, пожалуйста, добавьте в ответ. Это может быть полезно в некоторых других случаях.
д.путто
Достаточно справедливо, сделано.
Terdon

Ответы:

10

С awkвами можно сделать:

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


Объяснение: Первый awkаргумент ( out=file1) определяет переменную с именем файла, которая будет использоваться для вывода при обработке последующего аргумента ( largefile). awkПрограмма будет печатать все строки в файл , указанный в переменной out( {print >out}). Если шаблон XYZбудет найден, выходная переменная будет переопределена, чтобы указывать на новый файл ( {out="file2}"), который будет использоваться в качестве цели для печати последующих строк данных.

Ссылки:

Janis
источник
14

Это работа для csplit:

csplit -sf file -n 1 large_file /XYZ/

будет sпринудительно разбивать файл, создавая фрагменты с префиксом fix fileи nобъединяясь в одну цифру, например, file0и т. д. Обратите внимание, что использование /regex/будет разбивать до, но не включая совпадающую строку regex. Чтобы разделить и включить соответствие строк, regexдобавьте +1смещение:

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

Это создает два файла, file0и file1. Если вам абсолютно необходимо, чтобы они были названы, file1и file2вы всегда можете добавить пустой шаблон в csplitкоманду и удалить первый файл:

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

создает file0, file1и , file2но file0пуст , так что вы можете безопасно удалить его:

rm -f file0
don_crissti
источник
Это, я думаю, самый простой ответ. Все, что вам нужно сделать, это перечислить некоторые шаблоны, и файл будет разбит по ним по порядку. Brilliant!
Генри Блит
6

С современным kshвот вариант оболочки (т.е. без sed) одного из sedоснованных ответов выше:

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


И еще один вариант в kshодиночку (то есть также опуская cat):

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


(Чистое kshрешение кажется довольно производительным; для файла теста объемом 2,4 ГБ требовалось 19–21 с, по сравнению с 39–47 с при использовании подхода на основе sed/ cat).

Janis
источник
Это очень быстро Но я не думаю, что вам нужно, readи print- вы должны просто позволить ему пойти на вывод своих собственных. Производительность улучшается, если полностью собрать инструментарий AST и собрать все kshвстроенные модули - для меня странно, что sedна самом деле это не один из них. Но с такими вещами, как while <file doя думаю, тебе не нужно sedтак много ...
mikeserv
Мне любопытно, хотя - как вы awkвыступили в своем тесте? И хотя я уверен, что ksh, скорее всего, всегда выиграет этот бой, если вы используете GNU, с которым sedвы не очень честны sed- GNU -unbuffered - это плохой подход к POSIXLY, гарантирующий, что смещение дескриптора остается там, где программа закрывается это - не должно быть необходимости замедлять обычную работу программы - буферизация в порядке - все, что sedнужно сделать, это найти дескриптор, когда закончите. По какой-то причине GNU меняет этот менталитет.
mikeserv
@mikeserv; Сопоставление с шаблоном перенаправления выполняется до тех пор, пока шаблон не будет найден, и строка с найденным шаблоном не будет напечатана, если явно не будет сделано, как показано. (По крайней мере, это показало мой тест.) Обратите внимание, что нет while; печать неявно выполняется как определенный побочный эффект <##оператора перенаправления. И только соответствующая строка требует печати. (Таким образом, реализация функции оболочки наиболее гибкая для поддержки incl./excl.) Явный whileцикл, который я ожидаю, будет значительно медленнее (но не проверял).
Янис
1
@mikeserv; Ах хорошо. Кстати, я только что попробовал headвместо read; это только кажется , немного медленнее, но это terser код: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Янис
1
@mikeserv; Хорошая точка зрения; это не было Но когда я активирую встроенную функцию (только что сделал и проверил результаты), это странные цифры. (Может быть, некоторые служебные вызовы по сравнению с чтением?)
Janis
6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

С GNU sedвы должны использовать -uпереключатель nbuffered. Большинство других sedдолжно просто работать.

Чтобы оставить XYZ вне ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1
mikeserv
источник
3

Попробуйте это с помощью 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
don_crissti
1

Легкий взлом заключается в том, чтобы печатать в STDOUT или STDERR, в зависимости от того, был ли выбран целевой шаблон. Затем вы можете использовать операторы перенаправления оболочки для соответствующего перенаправления вывода. Например, в Perl предполагается, что вызывается входной файл, fа два выходных файла f1и f2:

  1. Отбрасывая линию, которая соответствует шаблону разделения:

    perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
  2. Включая согласованную линию:

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

В качестве альтернативы, распечатайте на разные дескрипторы файлов:

  1. Отбрасывая линию, которая соответствует шаблону разделения:

    perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
    if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
  2. Включая согласованную линию:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
terdon
источник