Zamień ciąg wielowierszowy w plikach

17

Mam wiele plików, które chcę zaktualizować, zastępując jeden ciąg wieloliniowy innym ciągiem wieloliniowym. Coś w stylu:

* Some text, 
* something else
* another thing

I chcę go zastąpić:

* This is completely
* different text

Rezultat byłby taki, że po zastąpieniu plik zawierający pierwszy blok tekstu będzie teraz zawierał drugi ciąg znaków (reszta pliku pozostanie niezmieniona).

Częścią problemu jest to, że muszę znaleźć listę plików do aktualizacji w systemie plików. Wydaje mi się, że mogę do tego użyć grep (choć znowu nie jest to tak łatwe w przypadku wieloliniowych łańcuchów), a następnie włożyć go w sed?

Czy jest na to łatwy sposób? Sed jest opcją, ale jest niewygodne, ponieważ muszę dodać \ n itp. Czy istnieje sposób, aby powiedzieć „weź dane wejściowe z tego pliku, dopasuj je w tych plikach, a następnie zastąp je treścią tego innego pliku”? W razie potrzeby mogę używać Pythona, ale chcę czegoś szybkiego i prostego, więc jeśli jest dostępne narzędzie, wolę go użyć niż napisać własny skrypt (co wiem, jak to zrobić).

ventsyv
źródło
Prawdopodobnie powinieneś do tego użyć Perla. stackoverflow.com/questions/1030787/…
orion
3
Więc chcesz dopasować, some text, something else another thingczy obejmuje wiele linii? A może chcesz tylko dopasować some text,\nsomething else\nanotherthing?
mikeserv
2
Edytuj swoje pytanie i wyjaśnij, jaka jest dokładnie zawartość każdego pliku (plików) i jakie są pożądane wyniki.
jimmij
Ciąg obejmuje wiele linii. Wolę nie brać pod uwagę białych znaków podczas dopasowywania / zastępowania, ponieważ nie wszystkie mogą być absolutnie takie same, ale nie jest to wielka sprawa, jeśli po prostu dopasuję 1-1 (nowe wiersze i wszystkie).
ventsyv

Odpowiedzi:

12

Zastąp „Some ... \ n ... Thing” zawartością pliku „new” w jednym lub kilku plikach wejściowych

perl -i -p0e 's/Some.*?thing\n/`cat new`/se' input.txt ...
  1. -i bezpośrednio zmienić input.txt
  2. -p0 plik wejściowy slurp i wydrukuj go na końcu
  3. s/regexp/.../s w wyrażeniu regularnym .jest.|\n
  4. s/.../exp/e zastąpiony przez eval(exp)
  5. nowy - plik zawierający tekst zastępczy (jest to całkowicie ... inny tekst)
  6. w razie potrzeby możesz rozwinąć oryginalny tekst s/Some text\n...\n...thing\n/...
JJoao
źródło
Jak mogę zrobić to samo z plikiem o nazwie powiedz „przed”, aby wyszukać (wielowierszową) zawartość tego pliku? Próbowałem, ale to nie działa.
Kvothe,
@Kvothe, potrzebujemy więcej szczegółów ... Zakładając, że „przed” nie ma żadnych specjalnych znaków, możesz spróbowaćperl -i -p0e ' $b= `cat before`; s/$b/Some thing\n/se' input.txt ...
JJoao
Zakładając, że „przed” zawiera wszystkie znaki specjalne (nowe wiersze, ukośniki, nawiasy kwadratowe) oprócz „i”.
Kvothe,
5
sed -e :n -e '$!N;/\n.*\n/!{$!bn
};  s/some text,\n* *something else\n* *another thing/this is completely\
different text/;P;D' <infile

Obawiam się, że będzie ci trudno znaleźć rozwiązanie, które ci odpowiada, dopóki nie opracujesz konkretnego opisu problemu - ale do tego najlepiej nadaje się QA, tak jak ja to widzę. Być może da ci to pewien pomysł - zawsze będzie trzymał 3 linie w przestrzeni wzorca na raz - z 2-liniowym spojrzeniem w przód - jednocześnie przesuwając do przodu plik wejściowy tylko linię na raz.

Powinien być w stanie dopasować ciąg, niezależnie od tego, czy obejmuje wiele linii, czy nie - to znaczy do trzech. Ale nie ma żadnych przepisów dotyczących dublowania tego przepisu w zastępstwie - zawsze obejmuje on dwa wiersze, jak napisano.

mikeserv
źródło
0

Nie za mocny (bo nie sprawdzaj drugiego ciągu, ale łatwo go osiedlić) i może nie być zgodny z posix, ale bardzo prosty:

sed '/^Some text/{:1;/another thing$/!{N;b 1}
     s/.*/this is completely\ndifferent text/g}' input.txt

Pierwsze polecenie dodaje linie z jakiegoś tekstu, dopóki nie spotka się z inną rzeczą, a następnie druga linia zmienia go na inny tekst.

UWAGA Ograniczeniem jest to, że po każdym tekście zawsze powinna następować inna rzecz .

Costas
źródło
Problem polega na tym, że ciąg może mieć więcej niż 2 linie (do kilkunastu) i może zawierać inne elementy, które mogą wymagać zmiany znaczenia, takie jak tabulatory, * itp.
ventsyv 26.01.2015
@ventyv Nie ma problemu z liczbą wierszy lub separatorów - skrypt sprawdza tylko początek i koniec. Wystarczy JEŚLI łańcuch początkowy wyjątkowo może oznaczać tekst do zmiany . Jeśli nie ma, lepiej pokaż przykładowy przykład, aby uzyskać prawidłowy wzór.
Costas