Использование sed и grep / egrep для поиска и замены

97

Я использую, egrep -Rза которым следует регулярное выражение, содержащее около 10 объединений, например: .jpg | .png | .gifи т.д. Это работает хорошо, теперь я хотел бы заменить все найденные строки на.bmp

Я думал о чем-то вроде

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

так что проблема здесь в том, как мне сделать аналогичное регулярное выражение объединения sedи как мне сказать ему сохранить изменения в файле, из которого он получил ввод.

Ори
источник
2
Я нашел этот вопрос во время поиска способов поиска и замены нескольких файлов в иерархии каталогов. Для других в моей ситуации попробуйте rpl .
titaniumdecoy
спасибо rpl работает и его действительно легко запомнить .. просто rpl old_string new_string target_files.
cesarpachon

Ответы:

183

Используйте эту команду:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: найти совпадающие строки с помощью расширенных регулярных выражений

    • -l: только список совпадающих имен файлов

    • -R: рекурсивный поиск по всем заданным каталогам

    • -Z: использовать \0как разделитель записей

    • "\.jpg|\.png|\.gif": Матч с одной из строк ".jpg", ".gif"или".png"

    • .: начать поиск в текущем каталоге

  • xargs: выполнить команду с аргументом stdin

    • -0: использовать \0как разделитель записей. Это важно, чтобы соответствовать -Zof egrepи не вводить в заблуждение пробелы и символы новой строки во входных именах файлов.

    • -l: использовать одну строку на команду в качестве параметра

  • sed: S Tream ред itor

    • -i: заменить входной файл выходным без резервного копирования

    • -e: используйте следующий аргумент как выражение

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': Заменить все вхождения строк ".jpg", ".gif"или ".png"с".bmp"

Дэвид Шмитт
источник
все работает кроме | в части sed. Я не понимаю, почему, хотя, поскольку это имеет смысл ... часть -l xargs выдавала мне ошибки, поэтому я удалил ее, может ли это быть связано?
Ori
Я обнаружил, что эта команда добавляет новую строку в конец всех файлов, которые она обрабатывает.
titaniumdecoy
@titanumdecoy: Мне не удалось воспроизвести такое поведение. какую версию sed вы использовали и на какой ОС?
Дэвид Шмитт
1
@DavidSchmitt: вы, вероятно, захотите использовать sed -rрасширенные регулярные выражения. В этот момент шаблон будет соответствовать тому, что используется в egrep, и вы можете захотеть поместить его в переменную для повторного использования.
bukzor
эта команда сэкономила мне часы работы по копированию файлов заголовков из моего приложения для созданной мной библиотеки. Это круто :) Вот команда, которую я использовал egrep -lRZ "\ .h $". | xargs -0 tar -cvf headers.tar | (cp headers.tar заголовки; cd заголовки; tar xf headers.tar;)
Lazy Coder
11

Честно говоря, как бы я ни любил sed для соответствующих задач, это определенно задача для perl - он действительно более мощный для такого типа однострочных сообщений, особенно для того, чтобы «записать его туда, откуда он пришел» ( -iпереключатель perl делает это для you, и, при желании, также позволяет вам сохранить старую версию, например, с добавленным .bak, просто используйте -i.bakвместо этого).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

а не замысловатую работу в sed (если там вообще возможно) или awk ...

Алекс Мартелли
источник
6
sed использует -i, как и perl.
Стобор,
@Stobor - клянусь, у меня были проблемы, когда операция perl, когда я вводил строку замены регулярного выражения, делала именно то, что я хотел, в отличие от sed, даже если я дал параметр регулярного выражения sed... Я думаю, что я либо забыл некоторые флаги для sed, либо у него были ограничения.
meder omuraliev
10

Другой способ сделать это

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Некоторая помощь относительно вышеуказанной команды

Находка выполнит поиск в текущем каталоге, обозначенном .

-name имя файла, в моем случае его pom.xml может давать подстановочные знаки.

-exec выполнять

sed редактор потока

-i игнорировать регистр

s для замены

/4.6.0.../ Строка для поиска

/5.0.0.../ Строка, которую нужно заменить

Сирил Чериан
источник
возможно, не так мощно, но для меня это намного легче понять, чем принятый ответ
icc97
5

Мне не удалось заставить ни одну из команд на этой странице работать на меня: решение sed добавило новую строку в конец всех обработанных файлов, а решение perl не смогло принять достаточное количество аргументов от find. Я нашел это решение, которое отлично работает:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

Это будет рекурсия вниз текущее дерева каталогов и заменить search_regexс replacement_stringв любых файлах , оканчивающихся на .hили .m.

Раньше я также использовал для этой цели rpl .

титан
источник
0

попробуйте что-нибудь, используя цикл for

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

не красиво, но должно сойтись.

Дон Йохе
источник
3
xargs здесь определенно более уместен.
Nathan Fellman
1
и использование |while read iшаблона позволит осуществлять потоковую передачу и избежать ограничений длины, когда результаты egrep становятся слишком длинными
Дэвид Шмитт
0

В моем случае я хотел заменить foo:/Drive_Letterна. foo:/bar/baz/xyz В моем случае я смог сделать это с помощью следующего кода. Я находился в том же месте каталога, где хранилась большая часть файлов.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

надеюсь, что это помогло.

ОБНОВЛЕНИЕ s | foo: / Буква_диска: | foo: / ba / baz / xyz | g

neo7
источник
Вы можете использовать другие разделители для команды sed, и это сделает пути намного лучше:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Кевин,