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


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

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

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

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

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

7 64

7 ответов:

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

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

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

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

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

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

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

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 подключения к серверу (возможно, есть способ сделать все это в одной строке), он также эффективно выполняет эту работу

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

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

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

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

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

${parameter/pattern/string}

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