Patrzyłem na to pytanie, a potem zastanawiałem się, jak zaimplementować moją odpowiedź, która używa
sed
wyłącznie
POSIX
ex
.
Sztuczka polega na tym, że chociaż sed
mogę porównać przestrzeń wstrzymania z przestrzenią wzorów, aby sprawdzić, czy są one dokładnie równoważne (z
G;/^\(.*\)\n\1$/{do something}
), nie znam sposobu na wykonanie takiego testu ex
.
Wiem, że w Vimie mogłem Y
obrócić pierwszą linię, a następnie napisać,
:2,$g/<C-r>0/d
by prawie zrobić to, co określam - ale jeśli pierwszy wiersz zawiera cokolwiek innego niż bardzo prosty tekst alfanumeryczny, staje się to rzeczywiście przypadkiem, ponieważ linia jest zrzucana jako
wyrażenie regularne , nie tylko ciąg znaków do porównania. (A jeśli pierwszy wiersz zawiera ukośnik, reszta wiersza zostanie zinterpretowana jako polecenie!)
Więc jeśli chcę usunąć wszystkie wiersze, myfile
które są identyczne z pierwszym wierszem - ale nie usunąć pierwszego - jak mogę to zrobić za pomocą ex
? Jeśli o to chodzi, jak mogę to zrobić za pomocą vi
?
Czy istnieje sposób POSIX na usunięcie linii, jeśli dokładnie pasuje do innej linii?
Być może coś w rodzaju tej wymyślonej składni:
:2,$g/**lines equal to "0**/d
źródło
:execute '2,$g/\V' . escape(getline(1), '\') . '/d'
sed
jako filtru od wewnątrzex
i uruchomienie całej mojejsed
odpowiedzi na całym buforze ... który oczywiście działałby (i w rzeczywistości jest przenośny w przeciwieństwie dosed -i
).<C-r>0
bardzo dobre. Nie jestem pewien, czy lepiej radzisz sobie tylko z poleceniami Ex, ponieważ musisz chronić znaki specjalne. Bez ograniczenia zgodnego z POSIX myślę, że użyłbyś bardzo nomagicznego przełącznika,\V
a następnie chroniłbyś odwrotny ukośnik (ponieważ zachowuje on swoje specjalne znaczenie nawet przy pomocy\V
) za pomocąescape()
funkcji, której drugi argument jest ciągiem zawierającym wszystkie znaki, które chcesz uciec / zabezpieczyć .:execute '2,$g/\V' . escape(getline(1), '\/') . '/d'
Lub możesz użyć innego znaku dla separatora wzorców, takiego jak średnik. W takim przypadku nie trzeba chronić ukośnika we wzorcu. Dałoby to coś takiego::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
sed
również bardzo dobre. Dzięki Vimowi często przekazujesz określone zadania specjalne innym programom ised
prawdopodobnie jest to dobry przykład. Nawiasem mówiąc, nie musisz uruchamiaćsed
całego bufora. Jeśli chcesz uruchomić go tylko na części bufora, możesz podać zakres. Na przykład, jeśli chcesz filtrować tylko te linie pomiędzy 50 a 100, można wpisać::50,100!<your sed command>
.Odpowiedzi:
Wigor
W Vimie możesz dopasować dowolny znak, w tym znak nowej linii
\_.
. Możesz użyć tego do skonstruowania wzoru pasującego do całej linii, dowolnej ilości rzeczy, a następnie tej samej linii:Teraz chcesz usunąć wszystkie wiersze w pliku, które pasują do pierwszego, nie włączając pierwszego. Podstawienie do usunięcia ostatniego wiersza pasującego do pierwszego to:
Możesz użyć,
:global
aby upewnić się, że podstawienie jest powtarzane wystarczająco dużo razy, aby usunąć wszystkie linie:POSIX ex
@saginaw pokazuje lepszy sposób na to w Vimie w komentarzu do twojego pytania, ale możemy dostosować powyższą technikę dla POSIX ex.
Aby to zrobić w sposób zgodny z POSIX, musisz wyłączyć dopasowanie wielu linii, ale nadal możesz korzystać z odwołań wstecznych. Wymaga to dodatkowej pracy:
Oto podział:
Więc jeśli bieżąca linia jest duplikatem linii 1, rejestr x będzie zawierał
d
. Wykonanie go spowoduje usunięcie bieżącej linii. Jeśli nie jest duplikatem, będzie zawierał bzdury z prefiksem, z"
którym po wykonaniu jest to brak operacji, ponieważ"
rozpoczyna komentarz. Nie wiem, czy jest to najładniejszy sposób, aby to osiągnąć, to tylko pierwszy, który przyszedł mi do głowy!Zdarza się, że nie można usunąć pierwszego wiersza, ponieważ proces kopiowania tymczasowo zmienia wiersz 1. Jeśli to nie był przypadek można poprzedzić
:g
z2,$
zakresu zamiast.Testowane w Vimie i ex-vi wersji 4.0.
EDYTOWAĆ
I prostszy sposób, który pozwala uniknąć znaków specjalnych w celu utworzenia wzorca wyszukiwania (z
'nomagic'
zestawem), buduje:global
polecenie, a następnie je wykonuje:Nie możesz tego zrobić jako liniowiec, ponieważ miałbyś zagnieżdżone
:global
, co jest niedozwolone.źródło
Wydaje się, że jedynym sposobem na to, aby to zrobić w POSIX, jest użycie zewnętrznego filtra, takiego jak
sed
.Na przykład, aby usunąć 17 linię pliku tylko wtedy, gdy jest dokładnie identyczna z linią 5, a poza tym pozostawić ją bez zmian, możesz wykonać następujące czynności:
(Możesz uruchomić
sed
cały bufor tutaj lub możesz uruchomić go tylko w wierszach 5-17, ale w pierwszym przypadku robisz niepotrzebne filtrowanie - nic wielkiego - a w tym drugim przypadku będziesz musiał użyć cyfry 1 i 13 w twoimsed
poleceniu zamiast 5 i 17. Mylące.)Ponieważ
sed
wykonuje tylko jedno przejście do przodu, nie ma łatwego sposobu na cofnięcie i usunięcie piątej linii tylko wtedy, gdy jest identyczna z 17-tą linią. Próbowałem przez chwilę z ciekawości ... to trudne .Przełom - możesz to zrobić w następujący sposób:
To właściwie bardziej ogólna metoda. Można go również użyć, aby dać ten sam wynik jak pierwsze polecenie (i usunąć 17 linię tylko, jeśli jest identyczna z 5 linią) w następujący sposób:
W przypadku szerszych zastosowań, takich jak usuwanie wszystkich wierszy pliku, które są identyczne z wierszem 37, pozostawiając nienaruszoną linię 37, możesz wykonać następujące czynności:
Zawarcie tu jest do sprawdzania, czy dwa wiersze są identyczne, najlepszym narzędziem jest
sed
, nieex
. Ale jak wspomniała DevSolar w komentarzu , nie jest to błądvi
aniex
- są one zaprojektowane do pracy z narzędziami uniksowymi; to jest główna siła.źródło