CLI AI

Найти и заменить во всех файлах проекта

2026-05-03

Команда — grep -rl 'old' . | xargs sed -i 's/old/new/g'. Важная половина здесь первая: grep -rl показывает, какие файлы изменятся, и вы успеваете посмотреть до того, как sed перепишет их на месте.

1. Какие файлы вообще затронет?

clai
$ clai покажи файлы внутри proj, в которых встречается old_name→ grep -rl 'old_name' proj/proj/src/b.pyproj/src/a.py

-r идёт вглубь, -l печатает имена файлов вместо совпавших строк. Два файла — правдоподобное число. Если написало 4000, шаблон слишком широкий, и вы только что сэкономили себе полдня.

2. Заменить ровно в этих файлах

clai
$ clai замени old_name на new_name во всех файлах внутри proj, где оно есть→ grep -rl 'old_name' proj/ | xargs sed -i 's/old_name/new_name/g'$ cat proj/src/a.pynew_name = 1

Открываются только совпавшие файлы. Если вместо этого пустить sed -i по всему дереву, вы перепишете время изменения у каждого файла репозитория, включая тысячи не изменившихся.

3. Пробелы в именах файлов ничего не сломают

clai
$ clai та же замена, но чтобы корректно работала с пробелами в именах→ grep -rlZ 'old_name' proj/ | xargs -0 -r sed -i 's/old_name/new_name/g'

-Z заставляет grep разделять имена нулевым байтом, а -0 — xargs этого ожидать. Без этой пары my file.py приезжает двумя аргументами, и sed не правит ни один. -r не даёт xargs запустить sed вообще без аргументов, если совпадений не нашлось.

4. Обойти каталоги, которые трогать нельзя

clai
$ clai замени, но пропусти node_modules, .git и vendor→ grep -rlZ --exclude-dir={.git,node_modules,vendor} 'old_name' . | xargs -0 -r sed -i 's/old_name/new_name/g'

Без исключений вы рано или поздно перепишете исходники зависимости, и дифф будет на 40 000 строк.

5. Только один тип файлов

clai
$ clai замени только в python-файлах→ grep -rlZ --include='*.py' 'old_name' . | xargs -0 -r sed -i 's/old_name/new_name/g'

--include — это белый список, близнец --exclude. Чем уже, тем безопаснее.

6. Сделать бэкап — и остаться переносимым

clai
$ clai сделай замену с бэкапом каждого файла, чтобы работало и на маке→ grep -rlZ 'old_name' proj/ | xargs -0 -r sed -i.bak 's/old_name/new_name/g'

sed -i.bak кладёт a.py.bak рядом с каждым изменённым файлом. Это единственный вариант написания, который работает и в GNU, и в BSD: macOS требует суффикс, Linux его принимает.

Грабли

  • Ваш шаблон — регулярка, а не строка. ., *, / и $ особенные. Замена config.yml попадёт и в configXyml; экранируйте или возьмите разделитель, которого нет в тексте: sed -i 's|old/path|new/path|g'.
  • Сначала коммит. У sed -i нет отмены. Чистый git status до замены превращает ошибку в git checkout ..
  • grep -l останавливается на первом совпадении в файле — за счёт этого он быстрый. На замену это не влияет: sed всё равно проходит все строки.

Похожие вопросы

Как посмотреть замену, не применяя её? Уберите -i и дайте напечатать: grep -rlZ 'old' . | xargs -0 sed 's/old/new/g' | less. Или grep -rn 'old' ., чтобы увидеть каждую строку с номером.

Почему ничего не произошло? Обычно xargs получил пустой список либо шаблон совпадает не с тем, что вы думаете. Запустите отдельно половину с grep -rl.

Можно то же самое через find? Да: find . -type f -name '*.py' -exec sed -i 's/old/new/g' {} +. Но он перепишет каждый подошедший по имени файл независимо от того, было ли в нём совпадение, — поэтому вариант с grep -rl предпочтительнее.

Читайте также

Опишите замену, прочитайте команду, которую напишет CliAI, и запустите. Установка одной строкой.