Как разбить большой файл на две части, по шаблону?
Приведенный пример file.txt
:
ABC
EFG
XYZ
HIJ
KNL
Я хочу разделить этот файл на XYZ
такой, который file1
содержит строки до XYZ
и остальные строки в file2
.
Как разбить большой файл на две части, по шаблону?
Приведенный пример file.txt
:
ABC
EFG
XYZ
HIJ
KNL
Я хочу разделить этот файл на XYZ
такой, который file1
содержит строки до XYZ
и остальные строки в file2
.
Ответы:
С awk
вами можно сделать:
awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile
Объяснение: Первый awk
аргумент ( out=file1
) определяет переменную с именем файла, которая будет использоваться для вывода при обработке последующего аргумента ( largefile
). awk
Программа будет печатать все строки в файл , указанный в переменной out
( {print >out}
). Если шаблон XYZ
будет найден, выходная переменная будет переопределена, чтобы указывать на новый файл ( {out="file2}"
), который будет использоваться в качестве цели для печати последующих строк данных.
Ссылки:
Это работа для csplit
:
csplit -sf file -n 1 large_file /XYZ/
будет s
принудительно разбивать файл, создавая фрагменты с префиксом f
ix 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
С современным 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
).
read
и print
- вы должны просто позволить ему пойти на вывод своих собственных. Производительность улучшается, если полностью собрать инструментарий AST и собрать все ksh
встроенные модули - для меня странно, что sed
на самом деле это не один из них. Но с такими вещами, как while <file do
я думаю, тебе не нужно sed
так много ...
awk
выступили в своем тесте? И хотя я уверен, что ksh
, скорее всего, всегда выиграет этот бой, если вы используете GNU, с которым sed
вы не очень честны sed
- GNU -u
nbuffered - это плохой подход к POSIXLY, гарантирующий, что смещение дескриптора остается там, где программа закрывается это - не должно быть необходимости замедлять обычную работу программы - буферизация в порядке - все, что sed
нужно сделать, это найти дескриптор, когда закончите. По какой-то причине GNU меняет этот менталитет.
while
; печать неявно выполняется как определенный побочный эффект <##
оператора перенаправления. И только соответствующая строка требует печати. (Таким образом, реализация функции оболочки наиболее гибкая для поддержки incl./excl.) Явный while
цикл, который я ожидаю, будет значительно медленнее (но не проверял).
head
вместо read
; это только кажется , немного медленнее, но это terser код: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3
.
Попробуйте это с помощью 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
Легкий взлом заключается в том, чтобы печатать в STDOUT или STDERR, в зависимости от того, был ли выбран целевой шаблон. Затем вы можете использовать операторы перенаправления оболочки для соответствующего перенаправления вывода. Например, в Perl предполагается, что вызывается входной файл, f
а два выходных файла f1
и f2
:
Отбрасывая линию, которая соответствует шаблону разделения:
perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
Включая согласованную линию:
perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
В качестве альтернативы, распечатайте на разные дескрипторы файлов:
Отбрасывая линию, которая соответствует шаблону разделения:
perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
Включая согласованную линию:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZ
линия быть включена в вывод или нет?