Jak grep i wymienić

251

Muszę rekurencyjnie wyszukać określony ciąg we wszystkich plikach i podkatalogach w katalogu i zastąpić ten ciąg innym ciągiem.

Wiem, że polecenie znalezienia może wyglądać następująco:

grep 'string_to_find' -r ./*

Ale jak mogę zastąpić każde wystąpienie string_to_findinnym ciągiem?

Billtian
źródło
Nie wierzę, że grep może to zrobić (mogę się mylić). Łatwiejszym sposobem byłoby użycie seda lub perla do zastąpienia
Memento Mori
2
Spróbuj użyćsed -i 's/.*substring.*/replace/'
Eddy_Em
2
@Eddy_Em To zastąpi całą linię zamień. Musisz użyć grupowania, aby uchwycić część linii przed i po podciągu, a następnie umieścić ją w linii zastępczej. sed -i 's/\(.*\)substring\(.*\)/\1replace\2/'
JStrahl,
1
Możliwy duplikat
użycia

Odpowiedzi:

248

Inną opcją jest użycie find, a następnie przekazanie go przez sed.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;
rezizter
źródło
34
W terminalu OS X 10.10 -iwymagany jest odpowiedni ciąg rozszerzenia parametru . Na przykład w find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" {} \;każdym razie podanie pustego ciągu nadal tworzy plik kopii zapasowej, inaczej niż opisano w podręczniku ...
Eonil
10
Dlaczego dostaję komunikat „błąd sed: RE: niedozwolona sekwencja bajtów”. I tak, dodałem -i ""dla OS X. Działa inaczej.
taco
2
Miałem problem nielegalnej sekwencji bajtów na MacOS 10.12, a to pytanie / odpowiedź rozwiązać mój problem: stackoverflow.com/questions/19242275/... .
abeboparebop 11.04.17
3
Dotyczy to każdego pliku, więc czasy pliku są modyfikowane; i konwertuje zakończenia linii z CRLFna LFw systemie Windows.
jww
183

Dostałem odpowiedź.

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'
Billtian
źródło
14
Spowoduje to dwukrotne skanowanie pasujących plików ... raz za pomocą, grepa następnie ponownie za pomocą sed. Zastosowanie findmetody jest bardziej wydajne, ale wspomniana metoda działa.
cmevoli
41
W systemie OS X musisz zmienić opcję, sed -i 's/str1/str2/g'aby sed -i "" 's/str1/str2/g'to działało.
jdf
6
@cmevoli za pomocą tej metody, grepprzegląda wszystkie pliki i sedskanuje tylko pliki pasujące do grep. W findmetodzie z drugiej odpowiedzi findnajpierw wyświetla listę wszystkich plików, a następnie sedskanuje wszystkie pliki w tym katalogu. Ta metoda niekoniecznie jest wolniejsza, zależy to od liczby dopasowań i różnic w prędkości wyszukiwania między sed, grepa find.
joelostblom
4
OTOH w ten sposób pozwala PRZEGLĄDAĆ to, co grep znajdzie PRZED zastąpieniem, znacznie zmniejszając ryzyko niepowodzenia, szczególnie dla regex n00bs takich jak ja
Lennart Rolland
2
Jest to również przydatne, gdy zamiana grep jest bardziej sprytna niż sed. Na przykład ripgrep jest posłuszny .gitignore, a sed nie.
user31389,
43

Możesz nawet zrobić to w ten sposób:

Przykład

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

Spowoduje to wyszukanie ciągu „ windows ” we wszystkich plikach względem bieżącego katalogu i zastąpienie „ windows ” ciągiem „ linux ” dla każdego wystąpienia ciągu w każdym pliku.

Dulith De Costa
źródło
2
Jest grepto przydatne tylko wtedy, gdy istnieją pliki, których nie należy modyfikować. Uruchomienie sedna wszystkich plikach zaktualizuje datę modyfikacji pliku, ale pozostawi zawartość bez zmian, jeśli nie ma żadnych dopasowań.
tripleee
@tripleee: Uważaj na ... ale [sed] pozostaw zawartość bez zmian, jeśli nie ma żadnych dopasowań " . Podczas używania -i, uważam, że sedzmienia czas pliku każdego dotkniętego pliku, nawet jeśli zawartość jest niezmieniona. sedrównież konwertuje zakończenia linii . nie używać sedw systemie Windows w repo Git, ponieważ wszystkie CRLFsą zmieniane LF.
JWW
To polecenie wymaga znaku „” po -i, aby wskazać, że żadne pliki kopii zapasowej nie zostaną utworzone po dokonaniu podstawienia w miejscu, przynajmniej w macosx. Sprawdź stronę podręcznika, aby poznać szczegóły. Jeśli chcesz wykonać kopię zapasową, w tym miejscu umieść rozszerzenie pliku do utworzenia.
spinyBabbler
31

Działa to najlepiej dla mnie w systemie OS X:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

Źródło: http://www.praj.com.au/post/23691181208/grep-replace-text-string-in-files

Marc Juchli
źródło
to jest doskonałe! współpracuje również z ag:ag "search" -l -r . | sort | uniq | xargs perl -e 's/search/replace' -pi
@sebastiankeller W Twojej komendzie Perla brakuje końcowego ukośnika, który jest błędem składni.
tripleee
3
Dlaczego jest to sort -unawet jego część? W jakich okolicznościach spodziewałbyś grep -rlsię, że utworzysz dwukrotnie tę samą nazwę pliku?
tripleee
5

Inne rozwiązania łączą składnie wyrażeń regularnych. Aby używać wzorców perl / PCRE zarówno do wyszukiwania, jak i zamiany oraz do przetwarzania tylko pasujących plików, działa to całkiem dobrze:

grep -rlZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

gdzie match1i match2są zwykle identyczne, ale match1można je uprościć, aby usunąć bardziej zaawansowane funkcje, które są istotne tylko w przypadku podstawienia, np. przechwytywanie grup.

Tłumaczenie: greprekurencyjnie i wyświetl listę plików pasujących do tego wzorca PCRE, oddzielonych nul, aby chronić znaki specjalne w nazwie pliku, a następnie potokuj nazwy plików, do xargsktórych oczekuje listy rozdzielonej nul, ale nic nie zrobi, jeśli nie otrzyma żadnych nazw, i przejdź perldo linii zastępczych, w których znaleziono dopasowania.

Dodaj Iprzełącznik, grepaby zignorować pliki binarne. Dla przypadku wrażliwych dopasowywania spadek iprzechodzenia z grep, i iflagę dołączony do ekspresji podstawienia, ale niei przełącznika na perlsiebie.

Walf
źródło
Sam Perl jest w stanie rekursować strukturę plików. W rzeczywistości istnieje narzędzie find2perldostarczane z Perlem, które robi takie rzeczy bez żadnych xargssztuczek.
tripleee
@tripleee findnie wyszukuje zawartości pliku, a chodzi o to, aby przetwarzać tylko pasujące pliki bez pisania programu w Perlu.
Walf,
Jest to dobre rozwiązanie dla systemu Windows, ponieważ pozwala uniknąć problemu rozwiązań konwersji bazujących na rozwiązaniach sed. Dzięki!
JamHandy
4

Zwykle nie z grep, ale raczej z sed -i 's/string_to_find/another_string/g'lub perl -i.bak -pe 's/string_to_find/another_string/g'.

minopret
źródło
3

Zachowaj ostrożność podczas używania findised w repo git! Jeśli nie wykluczysz plików binarnych, możesz skończyć z tym błędem:

error: bad index file sha1 signature 
fatal: index file corrupt

Aby rozwiązać ten błąd, musisz przywrócić sed , zastępując new_stringswój old_string. Spowoduje to przywrócenie zastąpionych ciągów, dzięki czemu wrócisz do początku problemu.

Prawidłowym sposobem wyszukiwania ciągu i zastępowania go jest pominięcie findi użyciegrep w celu zignorowania plików binarnych:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

Kredyty dla @hobs

tsveti_iko
źródło
1

Oto co bym zrobił:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

to będzie szukać wszystkich plików zawierających filenamew nazwie pliku, pod /path/to/dir, niż dla każdego pliku znaleźć, szukaj linii z searchstringi wymienić oldznew .

Chociaż jeśli chcesz pominąć wyszukiwanie określonego pliku z filenameciągiem w nazwie pliku, po prostu wykonaj następujące czynności:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

To zrobi to samo powyżej, ale do wszystkich plików znajdujących się pod /path/to/dir.

tinnick
źródło
0

Inną opcją byłoby po prostu użycie perla z globstar.

Włączenie shopt -s globstarw .bashrc(lub gdziekolwiek) pozwala** wzorzec globu dopasowywał rekursywnie wszystkie podkatalogi i pliki.

Zatem stosując perl -pXe 's/SEARCH/REPLACE/g' -i **rekurencyjnie zastąpić SEARCHzREPLACE .

-XFlaga mówi Perl „Wyłącz wszystkie ostrzeżenia” - co oznacza, że nie będą narzekać katalogów.

Globstar pozwala również robić rzeczy, jak sed -i 's/SEARCH/REPLACE/g' **/*.extgdybyś chciał wymienić SEARCHsię REPLACEwe wszystkich plikach z rozszerzeniem dzieci .ext.

GuiltyDolphin
źródło
„Inną opcją byłoby po prostu użycie perla z globstar ...” - Nie na maszynach Posixy, takich jak Solaris. Dlatego właśnie szukam grepi sed.
jww