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
$A
i / lub$B
. Im większe zrobisz te liczby, tym wolniej będzie ono rosło - ale możesz je zrobić dość duże.Można korzystać
gnu grep
z-A
i-B
drukować dokładnie te części pliku, który chcesz wykluczyć jednak dodać-n
przełącznik również drukować numery linii, a następnie sformatować wyjście i przekazać go jako skrypt polecenia, abysed
usunąć te linie:Powinno to również działać z plikami wzorców przekazywanymi np.
grep
Przez-f
:Myślę, że można to nieco zoptymalizować, jeśli zwinie dowolne trzy lub więcej kolejnych numerów linii w zakresy, tak aby mieć np.
2,6d
Zamiast2d;3d;4d;5d;6d
... chociaż jeśli dane wejściowe zawierają tylko kilka dopasowań, nie warto tego robić.Inne sposoby, które nie zachowują kolejności linii i są najprawdopodobniej wolniejsze:
z
comm
:comm
wymaga posortowanych danych wejściowych, co oznacza, że kolejność linii nie zostanie zachowana na końcowym wyjściu (chyba że plik jest już posortowany), więcnl
jest używana do numerowania linii przed sortowaniem,comm -13
drukuje tylko linie unikalne dla 2. PLIKU, a następniecut
usuwa część dodaną przeznl
(to znaczy pierwsze pole i separator:
)z
join
:źródło
comm
będzie szybsze niż oryginalne zsed
igrep
?Jeśli nie masz nic przeciwko użyciu
vim
:-Nes
włącza niekompatybilny, cichy tryb ex. Przydatne do pisania skryptów.+{command}
każ vimowi uruchomić{command}
na pliku.g/${PAT}/
- na wszystkich pasujących liniach/fff/
. Staje się to trudne, jeśli wzorzec zawiera znaki specjalne wyrażeń regularnych, których nie zamierzałeś traktować w ten sposób..-${B}
- od 1 linii powyżej tego.+${A}
- do 2 wierszy poniżej tego (patrz:he cmdline-ranges
te dwa)d
- usuń linie.+w !tee
następnie zapisuje na standardowe wyjście.+q!
kończy pracę bez zapisywania zmian.Możesz pominąć zmienne i bezpośrednio użyć wzorca i liczb. Użyłem ich tylko dla jasności celu.
źródło
Co powiesz na (używając GNU
grep
ibash
):Tutaj znajdujemy linie, które mają zostać odrzucone
grep -B2 -A1 'fff' file.txt
, a następnie wykorzystujemy to jako plik wejściowy, aby znaleźć żądane linie odrzucające je.źródło
kos
(teraz usunięte), tak jakby w pliku wejściowym były zduplikowane linie, a niektóre z nich wykraczają poza zakres, a inne znajdują się w tym zakresie, spowoduje to ich usunięcie. Ponadto, przy wielu wystąpieniach wzorca , jeśli--
w pliku wejściowym znajdują się linie (poza zakresami), zostaną one usunięte, ponieważ separator--
pojawia się nagrep
wyjściu, gdy więcej niż jedna linia pasuje do wzorca (ten drugi jest wysoce nieprawdopodobny, ale warty wspomnienie chyba).Możesz uzyskać wystarczająco dobry wynik, używając plików tymczasowych:
Wynik jest wystarczający, ponieważ możesz utracić wcięcie w procesie, ale jeśli jest to plik niewrażliwy na XML lub wcięcie, nie powinno to stanowić problemu. Ponieważ ten skrypt używa pamięci RAM, zapisywanie i odczytywanie plików tymczasowych jest tak szybkie, jak praca w pamięci.
źródło
Ponadto, jeśli chcesz wykluczyć niektóre wiersze przed danym znacznikiem, możesz użyć:
(glenn jackman na /programming//a/1492538 )
Pipingując niektóre polecenia, możesz uzyskać zachowanie przed / po:
źródło
awk
na odwróconym pliku do obsługi kolejnych linii, jeśli chcesz wpłynąć na linie przed i ponownie odwrócić wynik.Jednym ze sposobów na osiągnięcie tego, być może najłatwiejszym sposobem jest utworzenie zmiennej i wykonanie następujących czynności:
W ten sposób nadal masz swoją strukturę. I możesz łatwo zobaczyć z jednej wkładki, co próbujesz usunąć.
źródło
Jeśli jest tylko 1 dopasowanie:
W przeciwnym razie (awk):
źródło