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?


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' {} \;

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'

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.


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


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.


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'.


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


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.


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.


„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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.