Używanie sed i grep / egrep do wyszukiwania i zamiany

97

Używam, egrep -Rpo którym następuje wyrażenie regularne zawierające około 10 unii, na przykład: .jpg | .png | .gifitd. To działa dobrze, teraz chciałbym zamienić wszystkie znalezione ciągi na.bmp

Myślałem o czymś takim

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

więc problem polega na tym, jak zrobić podobne wyrażenie regularne unii w programie sedi jak powiedzieć mu, aby zapisał zmiany w pliku, z którego pobrał dane wejściowe.

Lub ja
źródło
2
Znalazłem to pytanie, szukając sposobów wyszukiwania i zastępowania wielu plików w hierarchii katalogów. Dla innych w mojej sytuacji wypróbuj rpl .
Titaniumdecoy
dziękuję rpl działa i jest naprawdę łatwy do zapamiętania ... po prostu rpl old_string new_string target_files.
cesarpachon

Odpowiedzi:

183

Użyj tego polecenia:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: znajdź pasujące wiersze przy użyciu rozszerzonych wyrażeń regularnych

    • -l: wyświetla tylko pasujące nazwy plików

    • -R: przeszukuje rekurencyjnie wszystkie podane katalogi

    • -Z: użyj \0jako separatora rekordów

    • "\.jpg|\.png|\.gif": Pasuje do jednego z ciągów ".jpg", ".gif"lub".png"

    • .: rozpoczęcie wyszukiwania w bieżącym katalogu

  • xargs: wykonuje polecenie ze stdin jako argumentem

    • -0: użyj \0jako separatora rekordów. Jest to ważne, aby dopasować -Zof egrepi uniknąć oszukania przez spacje i znaki nowej linii w nazwach plików wejściowych.

    • -l: użyj jednej linii na polecenie jako parametru

  • sed: The s TREAM wyd itor

    • -i: zamień plik wejściowy na wyjście bez tworzenia kopii zapasowej

    • -e: użyj następującego argumentu jako wyrażenia

    • 's/\.jpg\|\.gif\|\.png/.bmp/g'Zamienić wszystkie wystąpienia ciągów ".jpg", ".gif"lub ".png"z".bmp"

David Schmitt
źródło
to wszystko działa z wyjątkiem | w części sed. Nie rozumiem jednak dlaczego, skoro to ma sens ... -l część xargs dawała mi błędy, więc je usunąłem, czy to może być powiązane?
Ori
Odkryłem, że to polecenie dodaje nową linię na końcu wszystkich plików, które przetwarza.
titaniumdecoy
@titanumdecoy: Nie udało mi się odtworzyć tego zachowania. jakiej wersji seda używasz i na jakim systemie operacyjnym jesteś?
David Schmitt,
1
@DavidSchmitt: Prawdopodobnie chcesz używać sed -rrozszerzonych wyrażeń regularnych. W tym momencie wzorzec będzie pasował do tego, co jest używane w egrep i możesz chcieć umieścić go w zmiennej do ponownego wykorzystania.
bukzor
to polecenie zaoszczędziło mi wielu godzin pracy na kopiowaniu plików nagłówkowych z mojej aplikacji do utworzonej przeze mnie biblioteki. To jest niesamowite :) Oto polecenie, którego użyłem egrep -lRZ "\ .h $". | xargs -0 tar -cvf headers.tar | (cp headers.tar headers; cd headers; tar xf headers.tar;)
The Lazy Coder,
11

Szczerze mówiąc, tak jak uwielbiam seda za odpowiednie zadania, jest to zdecydowanie zadanie dla perla - jest to naprawdę potężniejsze dla tego rodzaju linijków, zwłaszcza, aby "zapisać to skąd pochodzi" ( -iprzełącznik Perla robi to dla you, a opcjonalnie pozwala również zachować starą wersję, np. z dołączonym .bak, po prostu użyj -i.bakzamiast tego).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

zamiast skomplikowanej pracy w sedzie (jeśli tam nawet jest to możliwe) lub awk ...

Alex Martelli
źródło
6
sed używa -i, tak jak perl.
Stobor
@Stobor - przysięgam, że miałem problemy z operacją perla, kiedy podałem ciąg zastępujący wyrażenie regularne, zrobiło dokładnie to, co chciałem, w przeciwieństwie do seda, nawet jeśli dałem opcję wyrażenia regularnego na sed... Myślę, że albo zapomniałem niektórych flag do seda lub miał ograniczenia.
meder omuraliev
10

Inny sposób, aby to zrobić

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Trochę pomocy odnośnie powyższego polecenia

Znalezisko wykona za Ciebie wyszukiwanie w bieżącym katalogu wskazanym przez .

-name nazwa pliku w moim przypadku pom.xml może zawierać symbole wieloznaczne.

-exec wykonać

sed edytor strumieni

-i ignoruj ​​wielkość liter

s jest zamiennikiem

/4.6.0.../ Ciąg do przeszukania

/5.0.0.../ Sznurek do wymiany

Cyril Cherian
źródło
może nie tak potężny - ale jest to dla mnie o wiele łatwiejsze do zrozumienia niż zaakceptowana odpowiedź
icc97
5

Nie mogłem sprawić, aby żadne z poleceń na tej stronie działało dla mnie: rozwiązanie sed dodało nową linię na końcu wszystkich przetwarzanych plików, a rozwiązanie Perl nie mogło zaakceptować wystarczającej liczby argumentów z find. Znalazłem takie rozwiązanie, które działa idealnie:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

To rekursja dół bieżącej drzewo katalogów i wymienić search_regexz replacement_stringna wszystkie pliki kończące się .hlub.m .

W przeszłości do tego celu używałem również rpl .

Titaniumdecoy
źródło
0

spróbuj czegoś za pomocą pętli for

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

nie ładne, ale powinno.

Don Johe
źródło
3
xargs jest tutaj zdecydowanie bardziej odpowiedni.
Nathan Fellman
1
i użycie |while read iwzorca umożliwiłoby przesyłanie strumieniowe i uniknęło ograniczeń długości, gdy wyniki egrep stałyby się zbyt długie
David Schmitt
0

Mój przypadek użycia polegał na tym, że chciałem zamienić foo:/Drive_Lettergo na foo:/bar/baz/xyz W moim przypadku udało mi się to zrobić za pomocą następującego kodu. Znajdowałem się w tym samym katalogu, w którym znajdowała się większość plików.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

mam nadzieję, że pomogło.

AKTUALIZACJA s | foo: / litera_dysku: | foo: / ba / baz / xyz | g

neo7
źródło
Możesz użyć innych ograniczników dla polecenia sed, a dzięki temu nazwy ścieżek są znacznie ładniejsze:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Kevin