Jak zamienić zawartość między dwoma wzorami z pliku?

9

Mam następujący format pliku:

<common>
fitnes=0
genetic=1
method=0
</common>
<inputs>
foo=bar
bar=foo
</inputs>
<limits>
balance=200.00
</limits>

i chciałbym usunąć wszystko, co jest pomiędzy <inputs>i </inputs>(z wyjątkiem samodzielnego wzorca) i zastąpić go treścią z innego pliku (np foo.txt.). Innymi słowy wiersze z foo=bari bar=foozostaną zastąpione inną treścią.

Prawdopodobnie może być podobny do sposobu usuwania dopasowania wieloliniowego , na przykład:

:g/<inputs/,/inputs>/d

ale nie jestem pewien, co powinienem zastąpić d, aby wstawić zawartość innego pliku, ale chcę zachować pasujący wzór.

Podobne podejście polegałoby na usunięciu wewnętrznej zawartości znacznika HTML , np

:/<inputs>/norm vitd

ale nie wiem, jak dodać do niego treść z pliku.

Idealnie staram się znaleźć jedną linijkę, ponieważ będzie ona częścią innego skryptu.

Jak mogę to osiągnąć?

kenorb
źródło

Odpowiedzi:

10

Podstawienia można użyć do zastąpienia wzorca wynikiem takiego wyrażenia:

:keeppatterns %s;<inputs>\zs\_.\{-}\ze</inputs>;\=insert(readfile('test.txt'), '')

Widzieć :help sub-replace-expression

Uwaga: keeppatternszapobiega substitutedodawaniu polecenia do historii wyszukiwania i zachowuje „rejestr ostatniego wzorca wyszukiwania” - patrz :help "/. Jest to dobra praktyka, aby uniknąć niepotrzebnego skryptów modyfikujących stan globalnej Vim jest z modyfikatorów dowodzenia keepalt, keepjumps, lockmarks, keepmarks, i keeppatterns.

djjcast
źródło
5

Alternatywne podejście Gary'ego to:

:g/^<inputs>/+,/^<\/inputs>/-d|-r dummy

Które najpierw usuwa wszystko z podanego wzorca, a następnie używa :rpolecenia do odczytu danych z manekina pliku. Teraz, jeśli musisz wyodrębnić nazwę pliku z linii między <input>/</input>wzorem, staje się to nieco bardziej skomplikowane.

Christian Brabandt
źródło
4

Wydaje się, że to działa, przynajmniej w systemie Unix:

:/<inputs>/+1,/<\/inputs>/-1!cat foo.txt

Używa {range}!{filter}polecenia do filtrowania linii od jednego <inputs>do drugiego </inputs>za pomocą zewnętrznego programu cat foo.txt. W takim przypadku catignoruje standardowe wejście, więc oryginalne wiersze są usuwane i zastępowane zawartością pliku foo.txt.

Widzieć:

:help :range!
:help cmdline-ranges
garyjohn
źródło
2

Myślę, że makro będzie łatwiejsze. Zwłaszcza jeśli masz różne miejsca do zrobienia tego samego.

  1. Pozostaw tylko te 2 pliki otwarte.
  2. Teraz w pierwszym pliku rozpocznij nagrywanie przez qa,( ato nazwa rekordu).
  3. Wyszukaj wzór początkowy, naciśnij w dół , a następnie naciśnij Ctrl- v, wyszukaj wzór końcowy, naciśnij w górę . Teraz wybrana jest część, którą chcesz usunąć. Usuń to.
  4. Zaznacz pozycję za pomocą ma( ato nazwa znaku) i zapisz plik.
  5. Przejdź do drugiego pliku, za pomocą :b2polecenia i podobnie wybierz część zgodnie z kryteriami początkowymi / końcowymi.
  6. Wróć do tego pliku, naciśnij, aaby przejść do tego punktu. Wklej tam p.
  7. Wyszukaj ponownie kryteria końcowe i przejdź przez sekcję, aby nie trafić ponownie.
  8. Wyjdź z przekodowywania naciskając q.
  9. Powtórz ile razy 40@a.
Krishanu Banerjee
źródło
1
Dzięki za sugestię, jest w porządku, ale w tym konkretnym przypadku nie pomoże, ponieważ szukam nieinteraktywnego rozwiązania w ramach exskryptu, w którym mogę określić, który plik do wstawienia w zależności od argumentów wejściowych użytkownika.
kenorb
1

Jeśli jest to kolejna próba parsowania XML za pomocą Narzędzi innych niż XML, nie rób tego.

Rozwiązania tutaj są poprawne, ale intencja może nie być. Jeśli XML nie pochodzi z dobrze oswojonego źródła, w tagu możesz otrzymać znaki ucieczki, CDATA lub białe spacje!

Więc xsltproc z polecenia w vi może sobie z tym poradzić.

Matthias Š
źródło
Plik nie jest właściwie poprawnym formatem XML, ale tak naprawdę nie jest dobrze udokumentowanym iniplikiem z tymi 3 znacznikami podobnymi do XML.
kenorb