Командная строка: поиск и замена во всех именах файлов, соответствующих grep

Я пытаюсь найти и заменить строку во всех файлах, соответствующих grep:

grep -n 'foo' * выдаст мне вывод в виде:

[filename]:[line number]:[text]

Для каждого файла, возвращаемого grep, я хотел бы изменить файл, заменив foo на bar.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
81
0
104 209
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Вы имеете в виду поиск и замену строки во всех файлах, соответствующих grep?

perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`

Редактировать

Поскольку это кажется довольно популярным вопросом, подумал, что обновлю.

Сейчас я в основном использую ack-grep, так как он более удобный. Таким образом, приведенная выше команда будет:

perl -p -i -e 's/old/new/g' `ack -l searchpattern`

Чтобы обработать пробелы в именах файлов, вы можете запустить:

ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

вы можете сделать больше с ack-grep. Допустим, вы хотите ограничить поиск только файлами HTML:

ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

А если пустое пространство не проблема, оно еще короче:

perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files

Я думаю, это может быть проблема с файлами / папками, содержащими пробелы. Can't open Untitled: No such file or directory, <> line 5 при попытке "Папка без названия / file.txt".

Xeoncross 07.12.2011 00:07

Если ваш sed(1) имеет опцию -i, используйте ее следующим образом:

for i in *; do
  sed -i 's/foo/bar/' $i
done

Если нет, есть несколько вариантов следующих вариантов в зависимости от того, на каком языке вы хотите играть:

ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
perl -pi.bak -e 's/foo/bar/' *
for i in *; do ... избыточен, sed может принимать список файлов в качестве аргументов.
Jens 01.06.2012 12:46

@Jens, почему бы не улучшить этот ответ, добавив свой собственный пример к приведенному выше?

Magpie 25.06.2014 15:43

Переменные всегда следует указывать в кавычках for i in *; do; sed -i 's/foo/bar/g' "$i"; done.

cat 06.07.2016 05:50

Судя по приведенному вами примеру, это именно то, что вам нужно:

sed -i 's/foo/bar/g' *

Он не рекурсивный (не будет переходить в подкаталоги). Для хорошего решения, заменяющего выбранные файлы по всему дереву, я бы использовал find:

find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

*.html - это выражение, которому файлы должны соответствовать, .bak после того, как -i создает копию исходного файла с расширением .bak (это может быть любое расширение, которое вам нравится), а g в конце выражения sed сообщает sed заменить несколько копий в одной строке (а не только в первой). -print для поиска - это удобный способ показать, какие файлы были сопоставлены. Все это зависит от конкретных версий этих инструментов в вашей системе.

Предупреждение для пользователей cygwin. Комбинация find и sed, похоже, меняет права пользователя на файлы, которые передаются через поток. Это можно просто исправить, используя команду chmod -R 644 * из того же уровня каталога, который использовался при работе find / sed.

kaskelotti 08.08.2013 14:30

Слово предупреждения людям, которые не хотят использовать аргумент -i: если вы его не используете, он не сработает (не спрашивайте меня, почему)

knocte 25.09.2013 17:41

@knocte -i сообщает sed изменить файл, в противном случае он просто выводит измененную версию на стандартный вывод. Если вы не хотите, чтобы файл .bak создавался, просто опустите часть «.bak», -i тоже работает автономно.

MattJ 25.09.2013 20:41

В OSX вам нужно указать команде find каталог для запуска, например find . -name '*.html' или find directoryname/ -name '*'.

Michiel Kauw-A-Tjoe 19.06.2014 12:06

мне нужно было добавить -e, иначе он подумал, что часть 's ...' была суффиксом sed -ie 's/foo/bar/g' *

vish 22.09.2014 08:36

"Foo", "bar" - это просто слова (для точного совпадения) или регулярное выражение?

KrishPrabakar 17.02.2020 16:51

@KrishPrabakar регулярное выражение

MattJ 20.02.2020 16:49

Мне нравится и я использовал вышеуказанное решение или общесистемный поиск и замену среди тысяч файлов:

find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;

Я предполагаю с помощью '* .htm?' вместо .html он ищет и находит как файлы .htm, так и .html.

Я заменяю .bak более общесистемной тильдой (~), чтобы упростить очистку файлов резервных копий.

find . -type f -print0 | xargs -0 <sed/perl/ruby cmd> будет обрабатывать несколько пространственных имен файлов одновременно, загружая по одному интерпретатору на пакет. Намного быстрее.

@knocte, «cmd» - это токен для всей команды поиска и замены для любого выбранного инструмента. Этот ответ отвечает на вопрос о том, как бороться с пустыми пространствами, содержащими имена файлов.

Tony Adams 03.11.2014 18:44

Это работает с использованием grep без использования perl или find.

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @

xargs не имеет -i в OSX или BSD openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/… вы имели в виду использовать верхний регистр «I»?

Tony Adams 03.11.2014 18:48

Я не знал, что -i не работает с другими ОС. У меня работает на убунту.

pymarco 03.11.2014 19:09

Моя справочная страница xargs (в Ubuntu) говорит, что -i устарел, и вместо этого нужно использовать -I. Итак, следует сказать: grep -rli 'old-word' * | xargs -I filepath sed -i 's/old-word/new-word/g' filepath

Max Wallace 25.06.2016 16:22

Уже дан ответ об использовании найти и sed

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

наверное стандартный ответ. Или вы можете использовать perl -pi -e s/foo/bar/g' вместо команды sed.

Для наиболее быстрого использования вы можете обнаружить, что команду rpl легче запомнить. Вот замена (foo -> bar) рекурсивно для всех файлов в текущем каталоге:

rpl -R foo bar .

По умолчанию он недоступен в большинстве дистрибутивов Linux, но его можно быстро установить (apt-get install rpl или аналогичный).

Однако для более сложных задач, связанных с регулярными выражениями и обратной подстановкой или переименованием файлов, а также с поиском и заменой, наиболее общий и мощный инструмент, о котором я знаю, - это Repren, небольшой скрипт Python, который я написал некоторое время назад для некоторых более сложные задачи переименования и рефакторинга. Причины, по которым вы можете предпочесть это:

  • Поддержка переименования файлов, а также поиска и замены содержимого файлов (включая перемещение файлов между каталогами и создание новых родительских каталогов).
  • Просмотрите изменения, прежде чем совершить поиск и замену.
  • Поддержка регулярных выражений с обратной подстановкой, целыми словами, без учета регистра и с сохранением регистра (замените foo -> bar, Foo -> Bar, FOO -> BAR).
  • Работает с несколькими заменами, включая свопы (foo -> bar и bar -> foo) или наборы неуникальных замен (foo -> bar, f -> x).

Проверьте README для примеров.

На самом деле это проще, чем кажется.

grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
  • grep рекурсивно просматривает ваше дерево (-R) и печатает только имя файла (-l), начиная с текущего каталога (./)
  • который передается по конвейеру в xargs, который обрабатывает их по одному (-n 1) и использует% в качестве заполнителя (-I%) в команде оболочки (sh -c)
  • в команде оболочки сначала печатается имя файла (ls%;)
  • затем sed выполняет встроенную операцию (-i), замену ('s /') foo на bar (foo / bar), глобально (/ g) в файле (опять же, представленный%)

Очень просто. Если вы хорошо разбираетесь в find, grep, xargs, sed и awk, почти нет ничего невозможного, когда дело доходит до манипулирования текстовыми файлами в bash :)

Ба, не обращайте внимания, этот pymarco уже говорил об этом выше. Оставляю свой ответ для объяснения.

siliconrockstar 19.11.2015 06:37

Другие вопросы по теме