Использование 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, совпадает шаблона удаляются, а следующий шаблон /
может быть опущен. Если параметр @
или *
, операция замены применяется по каждому позиционному параметру в свою очередь, и расширение это результирующий список. Если параметр - это переменная массива с индексом @
или *
, операция подстановки применяется к каждый член массива в свою очередь, а расширение - это результирующий список.