розділіть файл на дві частини за шаблоном


14

Як розділити великий файл на дві частини за шаблоном?

Наведений приклад file.txt:

ABC
EFG
XYZ
HIJ
KNL

Я хочу розділити цей файл на XYZтакий, який file1містить рядки вгору XYZта решта рядків у file2.


Чи XYZслід включати рядок у висновок чи ні?
тердон

@terdon У моєму випадку жоден рядок "XYZ" не повинен бути частиною file2. Але якщо у вас є спосіб зробити це, будь ласка, додайте відповідь. Це може бути корисно в деяких інших випадках.
д.путо

Досить справедливо, зроблено.
тердон

Відповіді:


10

З awkвами можна робити:

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


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

Список літератури:


14

Це робота для csplit:

csplit -sf file -n 1 large_file /XYZ/

буде silently розбити файл, створюючи шматки попередньо 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

Це, я думаю, найпростіша відповідь. Все, що вам потрібно зробити, це перерахувати деякі шаблони, і файл буде розділений ними за порядком. Блискуче!
Генрі Бліт

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на основі).


Це дуже швидко. Але я не думаю, що цього потрібно readі printпотрібно просто відпустити, щоб вивести все своє. Продуктивність покращується, якщо ви повністю складете інструментарій AST і отримаєте всі kshвбудовані складені файли - мені дивно, що sedнасправді це не одна з них. Але з такими речами, як while <file doя думаю, вам не потрібно sedтак багато ...
mikeserv

Мені цікаво хоч - як awkвиступили у вашому бенчмарці? І хоча я майже впевнений, що ksh, ймовірно, завжди виграє цю боротьбу, якщо ви використовуєте GNU, sedви не дуже справедливі sed- -unbuferi GNU - це поганий підхід до POSIXLY, гарантуючи, що компенсація дескриптора залишиться там, де програма кинеться це - не повинно бути сповільнювати регулярну роботу програми - буферизація прекрасна - все, що sedпотрібно робити, - це шукати дескриптор, коли закінчите. З будь-якої причини GNU повертає цю ментальність.
mikeserv

@mikeserv; Узгодження шаблону перенаправлення здійснюється до тих пір, поки шаблон не знайдеться, і рядок із знайденим шаблоном не буде надруковано, якщо не буде зроблено явно, як зображено. (Принаймні, це показав мій тест.) Зауважте, що немає while; друк неявно робиться як визначений побічний ефект <##оператора перенаправлення. І тільки друкована лінія потребує друку. (Таким чином, реалізація функції оболонки найбільш гнучка для підтримки incl./excl.) Явний whileцикл, який я очікував би, буде значно повільнішим (але не перевіряв).
Яніс

1
@mikeserv; Ага, гаразд. До речі, я просто спробував headзамість цього read; це здається лише трохи повільніше, але це більш короткий код : { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Яніс

1
@mikeserv; Гарна думка; це не було Але коли я активую вбудований (щойно зробив і перевірив результати), це дивно ті ж цифри. (Може бути, якийсь функціональний виклик над головою порівняно з прочитаним?)
Яніс

6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

За допомогою GNU sedслід використовувати -unbuferi-перемикач. Більшість інших seds повинні просто працювати, хоча.

Щоб вийти з XYZ ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.