Używając powłoki typu bash lub zshell, w jaki sposób mogę wykonać rekurencyjne polecenie „znajdź i zamień”? Innymi słowy, chcę zastąpić każde wystąpienie „foo” słowem „bar” we wszystkich plikach w tym katalogu i jego podkatalogach.
bash
shell
zsh
find-and-replace
Nathan Long
źródło
źródło
Odpowiedzi:
To polecenie to zrobi (przetestowane zarówno w systemie Mac OS X Lion, jak i Kubuntu Linux).
Oto jak to działa:
find . -type f -name '*.txt'
znajduje w bieżącym katalogu (.
) i poniżej wszystkie zwykłe pliki (-type f
), których nazwy kończą się na.txt
|
przekazuje dane wyjściowe tego polecenia (lista nazw plików) do następnego poleceniaxargs
zbiera te nazwy plików i podaje je jeden po drugimsed
sed -i '' -e 's/foo/bar/g'
oznacza „edytuj plik na miejscu, bez kopii zapasowej, i wykonaj następujące zastąpienie (s/foo/bar
) wiele razy w wierszu (/g
)” (patrzman sed
)Zauważ, że część „bez kopii zapasowej” w linii 4 jest dla mnie OK, ponieważ zmieniane przeze mnie pliki i tak są pod kontrolą wersji, więc mogę łatwo cofnąć, jeśli wystąpił błąd.
źródło
-print0
opcji. Twoje polecenie zawiedzie w przypadku plików ze spacjami itp. W ich nazwie.find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +
zrobi to wszystko z GNU find.sed: can't read : No such file or directory
kiedy uruchamiamfind . -name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g'
, alefind . -name '*.md' -print0
daje listę wielu plików.-i
i''
''
posed -i
co jest''
rola?To usuwa
xargs
zależność.źródło
sed
, więc zawiedzie w większości systemów. GNUsed
wymaga, abyś nie umieszczał spacji między-i
i''
.Jeśli używasz Git, możesz to zrobić:
-l
wyświetla tylko nazwy plików.-z
wypisuje bajt zerowy po każdym wyniku.Skończyło się na tym, ponieważ niektóre pliki w projekcie nie miały nowej linii na końcu pliku, a sed dodał nową linię, nawet jeśli nie wprowadził żadnych innych zmian. (Brak komentarza na temat tego, czy pliki powinny mieć nowy wiersz na końcu.)
źródło
find ... -print0 | xargs -0 sed ...
rozwiązań nie tylko zajmuje dużo dłużej, ale także dodaje nowe wiersze do plików, które jeszcze go nie mają, co jest uciążliwe podczas pracy w repozytorium git.git grep
dla porównania szybko się świeci.Oto moja funkcja zsh / perl, której używam do tego:
I wykonałbym to za pomocą
(na przykład)
źródło
Próbować:
Testowane na Ubuntu 12.04.
To polecenie NIE będzie działać, jeśli nazwy podkatalogów i / lub nazwy plików zawierają spacje, ale jeśli je masz, nie używaj tego polecenia, ponieważ nie będzie działać.
Zasadą jest używanie spacji w nazwach katalogów i nazw plików.
http://linuxcommand.org/lc3_lts0020.php
Zobacz „Ważne fakty dotyczące nazw plików”
źródło
Użyj tego skryptu powłoki
Teraz używam tego skryptu powłoki, który łączy rzeczy, których nauczyłem się z innych odpowiedzi i z przeszukiwania sieci. Umieściłem go w pliku o nazwie
change
w folderze$PATH
i tak zrobiłemchmod +x change
.źródło
Mój przypadek użycia była chciałem wymienić
foo:/Drive_Letter
zfoo:/bar/baz/xyz
W moim przypadku udało mi się to zrobić za pomocą następującego kodu. Byłem w tym samym katalogu, w którym było mnóstwo plików.mam nadzieję, że pomogło.
źródło
Poniższe polecenie działało dobrze w Ubuntu i CentOS; jednak w OS XI ciągle pojawiały się błędy:
Kiedy próbowałem przekazać parametry przez xargs, działało dobrze bez błędów:
źródło
-i
się-i ''
to chyba bardziej istotne niż fakt, że zmieniłeś-exec
się-print0 | xargs -0
. BTW, prawdopodobnie nie potrzebujesz-e
.Powyższe działało jak urok, ale z połączonymi katalogami muszę dodać
-L
do niego flagę. Ostateczna wersja wygląda następująco:źródło