Дон може бути кращим у більшості випадків, але про всяк випадок, якщо файл дійсно великий, і ви не можете отримати sedобробку такого великого файлу сценарію (що може траплятися приблизно в 5000+ рядків сценарію) , ось це просто sed:
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Це приклад того, що називається розсувним вікном на вході. Він працює, будуючи буфер рядків з рахунком вперед,$B перш ніж щось намагатися надрукувати.
Насправді, мабуть, я повинен уточнити свій попередній пункт: основний обмежувач продуктивності і для цього рішення, і для дона буде безпосередньо пов'язаний з інтервалом. Це рішення сповільнюватиметься з більшими розмірами інтервалів , тоді як донські сповільнюватимуться з більшими інтервальними частотами . Іншими словами, навіть якщо вхідний файл дуже великий, якщо фактичний інтервал зустрічається ще дуже рідко, то його рішення, ймовірно, має пройти. Однак якщо розмір інтервалу є відносно керованим і, ймовірно, трапляється часто, то це рішення, яке ви повинні вибрати.
Ось ось робочий процес:
- Якщо
$matchвін знайдеться в просторі шаблону, який передує \newline, sedбуде рекурсивно вибирати Dкожну \nлінію ewline, яка їй передує.
$matchРаніше я повністю очищав простір шаблону - але, щоб легко впоратися з перекриттям, залишається орієнтир, який працює набагато краще.
- Я також намагався
s/.*\n.*\($match\)/\1/спробувати отримати його за один раз і відхилити цикл, але коли $A/$Bвони великі, Dелегантний цикл виявляється значно швидше.
- Потім ми вводимо
Nрядок вхідного сигналу, який передує \nроздільнику ewline, і намагаємося ще раз Dвибрати /\n.*$match/його, звернувшись до нашого нещодавно використовуваного регулярного виразу w / //.
- Якщо простір шаблону збігається,
$matchтоді це можна зробити лише вгорі лінії $match- всі $Bпопередні лінії були очищені.
- Тож ми починаємо перебирати далі
$A.
- Кожен запуск цього циклу ми будемо намагатися
s///ubstitute для &себе $Aго \nсимволу ewline в просторі картини, і, в разі успіху, tЕст виросте нас - і весь наш $Aвіслюку буфер - зі сценарію повністю запустити скрипт через зверху з наступним рядком введення, якщо такий є.
- Якщо
test не є успішним, ми bповернемося до :tмітки op і повторимо для іншого рядка введення - можливо, запустимо цикл, якщо він $matchвідбудеться під час збору $After.
- Якщо пройти в
$matchпетлю функції, то ми будемо намагатися pРінту в $останній рядок , якщо це, і якщо !не намагатися s///ubstitute для &себе $Bго \newline характер в просторі картини.
- Ми також
tоцінимо це, і якщо це буде успішно, ми перейдемо на :Pярлик rint.
- Якщо ні, ми повернемось до
:tоп і отримаємо інший рядок введення, доданий до буфера.
- Якщо ми зробимо це для
:Pрингту, ми будемо Pринг, а потім Dпіднімемо до першого \newline у просторі шаблону і повторимо сценарій зверху з тим, що залишилося.
І так цього разу, якби ми робили A=2 B=2 match=5; seq 5 | sed...
Простір шаблону для першої ітерації в :Print виглядатиме так:
^1\n2\n3$
Ось як sedзбирає свій $Bпопередній буфер. І так sedдрукує $Bрядки виводу- рахунку за зібраним входом. Це означає , що, з огляду на наш попередній приклад, sedбуде PРінту 1до виходу, а потім Dдаліть що і відправити назад в початок сценарію шаблону простір , яке виглядає наступним чином :
^2\n3$
... а вгорі сценарію Nвиводиться рядок введення ext, і наступна ітерація виглядає так:
^2\n3\n4$
І тому, коли ми знаходимо перше виникнення 5введення, простір шаблону насправді виглядає так:
^3\n4\n5$
Потім Dзапускається цикл elete, і коли він проходить, це виглядає так:
^5$
І коли Nвиведений рядок введення ext sedвдаряє EOF і вимикається. На той час він лише колись Pпередзвонив рядки 1 і 2.
Ось приклад запуску:
A=8 B=7 match='[24689]0'
seq 100 |
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Це відбитки:
1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100