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

Я использую следующее для поиска рекурсивно для конкретной строки и замены ее другим:

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g'

Это работает нормально. Единственная проблема в том, что если строка не существует, то sed терпит неудачу, потому что она не получает никаких аргументов. Это проблема для меня, так как я запускаю это автоматически с помощью ANT, и сборка завершилась с ошибкой, так как sed не работает.

Есть ли способ сделать его отказоустойчивым, если строка не найдена?

Мне интересно однострочное простое решение, которое я могу использовать (не обязательно с grep или sed, но с общими командами unix, подобными этим).

Ответ 1

Вы можете использовать find и -exec непосредственно в sed, а не сначала размещать oldstr с помощью grep. Это может быть немного менее эффективно, но это может быть неважно. Таким образом, замена sed выполняется по всем файлам, указанным в find, но если oldstr не существует, то явно не будет работать на нем.

find /path -type f -exec sed -i 's/oldstr/newstr/g' {} \;

Ответ 2

Ваше решение в порядке. попробуйте сделать это следующим образом:

files=$(grep -rl oldstr path) && echo $files | xargs sed....

тогда выполните xargs только тогда, когда grep return 0, например. при обнаружении строки в некоторых файлах.

Ответ 3

Стандарт xargs не имеет хорошего способа сделать это; вам лучше использовать find -exec, как кто-то предложил, или обернуть sed в script, который ничего не делает, если аргументов нет. GNU xargs имеет параметр --no-run-if-empty, а BSD/OS X xargs имеет параметр -L, который выглядит так, как будто он должен делать что-то подобное.

Ответ 4

Я взял идею Влада и немного изменил ее. Вместо

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null

Что дает

sed: couldn't edit /dev/null: not a regular file

Я делаю в 3 разных подключениях к удаленному серверу

touch deleteme
grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' ./deleteme
rm deleteme

Хотя это менее элегантно и требует еще 2 подключения к серверу (возможно, есть способ сделать все это в одной строке), он также эффективно выполняет работу

Ответ 5

Я думаю, что без использования -exec вы можете просто предоставить /dev/null как минимум один аргумент, если ничего не найдено:

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null

Ответ 6

Моим вариантом использования я хотел заменить 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'

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

Ответ 7

Если вы хотите заменить фиксированную строку или какой-либо шаблон, я также хотел бы добавить конструкцию замещения переменной замены строки w50 > builtin pattern string. Вместо того, чтобы самому описать это, я цитирую раздел из руководства bash:

${parameter/pattern/string}

Шаблон расширяется, чтобы создать шаблон так же, как в pathname               расширение. параметр расширен, и самое длинное совпадение шаблона с его значением заменяется на строку. Если шаблон               начинается с /, все совпадения шаблона заменяются строкой.               Обычно заменяется только первое совпадение. Если начинается шаблон               с #, он должен совпадать в начале расширенного значения               параметр. Если шаблон начинается с %, он должен совпадать в конце               расширенного значения параметра. Если строка имеет значение null, совпадает               шаблона удаляются, а следующий шаблон / может быть опущен. Если параметр @ или *, операция замены               применяется по каждому позиционному параметру в свою очередь, и расширение               это результирующий список. Если параметр - это переменная массива с индексом @ или *, операция подстановки применяется к               каждый член массива в свою очередь, а расширение - это               результирующий список.