don może być lepszy w większości przypadków, ale na wypadek, gdyby plik był naprawdę duży i nie możesz sobie sed
poradzić z tak dużym plikiem skryptu (co może się zdarzyć przy ponad 5000 liniach skryptu) , oto proste 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'
Jest to przykład tak zwanego przesuwanego okna na wejściu. Działa poprzez budowanie bufora wyprzedzającego z $B
-count linii przed próbą wydrukowania czegokolwiek.
I właściwie powinienem wyjaśnić moją poprzednią kwestię: główny ogranicznik wydajności zarówno dla tego rozwiązania, jak i dla dona, będzie bezpośrednio związany z interwałem. To rozwiązanie będzie spowalniać przy większych rozmiarach interwałów , podczas gdy don będzie spowalniać przy większych częstotliwościach interwałów . Innymi słowy, nawet jeśli plik wejściowy jest bardzo duży, jeśli faktyczne występowanie interwału jest nadal bardzo rzadkie, jego rozwiązanie jest prawdopodobnie dobrym rozwiązaniem. Jeśli jednak wielkość interwału jest względnie łatwa do zarządzania i często występuje, należy wybrać to rozwiązanie.
Oto przepływ pracy:
- Jeśli
$match
zostanie znaleziony w przestrzeni wzorca poprzedzonej \n
ewline, sed
rekurencyjnie usunie D
każdą \n
ewline, która go poprzedza.
- Wyczyściłem
$match
przestrzeń wzorów całkowicie wcześniej - ale aby łatwo poradzić sobie z nakładaniem się, pozostawienie punktu orientacyjnego wydaje się działać znacznie lepiej.
- Próbowałem też
s/.*\n.*\($match\)/\1/
spróbować za jednym razem i uniknąć pętli, ale gdy $A/$B
są duże, D
pętla elete okazuje się znacznie szybsza.
- Następnie wyciągnij w
N
linii EXT wejścia poprzedzone \n
separatora ewline i spróbuj ponownie D
suĹ /\n.*$match/
ponownie odwołując się do naszej ostatnio używanego wyrażenia regularnego w / //
.
- Jeśli przestrzeń wzoru pasuje,
$match
to można to zrobić tylko $match
na początku linii - wszystkie wcześniejsze $B
linie zostały usunięte.
- Zaczynamy więc
$A
zapętlać.
- Każdy przebieg tej pętli będziemy próbować
s///
ubstitute na &
sobie $A
th \n
charakter ewline w przestrzeni wzorca, a jeśli się powiedzie, t
est nas oddział - i cały nasz $A
bufor fter - z skryptu w całości, aby uruchomić skrypt w ciągu od góry z następnym wierszem wprowadzania, jeśli istnieje.
- Jeśli
t
est się nie powiedzie, b
wrócimy do :t
etykiety operacji i przejdziemy do innej linii danych wejściowych - być może zaczniemy pętlę, jeśli $match
wystąpi podczas zbierania $A
fter.
- Jeśli przejdziemy przez
$match
pętlę funkcji, spróbujemy p
przeszukać $
ostatnią linię, jeśli to jest, a jeśli !
nie, postaramy się s///
zbudować dla &
siebie znak $B
tej \n
linii w przestrzeni wzorca.
- Będziemy
t
est to także, a jeśli jest to sukces będziemy rozgałęziać na :P
etykiecie rint.
- Jeśli nie, wrócimy do
:t
operacji i otrzymamy kolejną linię wejściową dołączoną do bufora.
- Jeśli zrobimy to do
:P
rintowania, zrobimy P
to następnie D
do pierwszej \n
ewline w przestrzeni wzorów i ponownie uruchom skrypt od góry z tym, co pozostało.
I tym razem, gdybyśmy to robili A=2 B=2 match=5; seq 5 | sed...
Przestrzeń wzorów dla pierwszej iteracji w :P
rint wyglądałaby następująco:
^1\n2\n3$
I w ten sposób sed
gromadzi swój $B
bufor. I tak sed
drukuje do wyjścia $B
-count linii za wejście to zebrał. Oznacza to, że biorąc pod uwagę nasz poprzedni przykład, sed
by P
rukuj 1
do wyjścia, a następnie D
suĹ że i wysłać z powrotem do górnej części skryptu przestrzeń która wygląda jak wzór:
^2\n3$
... a na górze skryptu N
pobierana jest linia wejściowa ext, więc następna iteracja wygląda następująco:
^2\n3\n4$
Kiedy więc znajdziemy pierwsze wystąpienie danych 5
wejściowych, przestrzeń wzoru wygląda tak:
^3\n4\n5$
Następnie D
uruchamia się pętla elete i po jej zakończeniu wygląda następująco:
^5$
A kiedy N
linia wejściowa ext zostanie wyciągnięta, sed
uderza EOF i kończy pracę. Do tego czasu ma tylko P
linie w odcieniach 1 i 2.
Oto przykładowy przebieg:
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'
To drukuje:
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