Вопрос или проблема
Пытаюсь протестировать, что мой sed работает, фильтруя файлы снова с помощью grep.
Могут быть трудные, но лаконичные способы, но я предпочел бы какой-то идиоматический способ запомнить.
Это не срабатывает:
find src -type f -name "*.py" -not -name "*.pyc" -exec sed -E "" "s/basicConfig/setLevel/g" {} + -exec grep "setLevel" {} +
Это, похоже, не срабатывает, потому что grep ищет в немодифицированных файлах, а не в измененном потоке.
Можно использовать -nE
, чтобы убрать раздражающий (в данном случае) вывод sed, но тогда все равно будет искать в оригинальных файлах.
Если я передам вывод (просто используя -E
), он все равно ничего не находит.
Я думаю, что возможно, find не поддерживает каналы, так как предоставляет -exec
.
Следующим шагом было попробовать модификацию предложения из поста на SO, который использует оболочку:
find src -type f -name "*.py" -not -name "*.pyc" -exec sh -c "sed -E '' 's/basicConfig/setLevel/g' | grep 'setLevel'" {} +
Но я получаю: sed: s/basicConfig/setLevel/g: Нет такого файла или каталога
Использую MacOS, но не стесняйтесь отвечать для любого UNIX.
Это сработало:
find src -type f -name "*.py" -not -name "*.pyc" -exec sh -c "sed 's/basicConfig/setLevel/g' {} | grep setLevel" \;
Или с -i
, но с ним не следует тестировать
find src -type f -name "*.py" -not -name "*.pyc" -exec sh -c "sed -i '' 's/basicConfig/setLevel/g' {} | grep setLevel" \;
''
нужно, чтобы сказать sed не создавать резервные файлы в MacOS, поэтому это не нужно с -E
(который упоминается в посте и на самом деле был для другого случая и был скопирован.)
В этих случаях {}
помещалось там, где должны быть имена файлов, и он заканчивается \;
.
Если вы хотите передать вывод find
и все команды, которые он выполняет, просто сделайте:
find src -type f -name '*.py' ! -name '*.pyc' \
-exec sed s/basicConfig/setLevel/g {} + |
grep setLevel
Но если вы хотите печатать только строки, которые изменяются, просто скажите sed
об этом, нет необходимости в grep
:
find src -type f -name '*.py' ! -name '*.pyc' \
-exec sed -n s/basicConfig/setLevel/gp {} +
-E
включают расширенные регулярные выражения, но вы не используете никаких операторов регулярных выражений, ни расширенных, ни обычных.
Этот лишний пустой аргумент, который вы добавляете, не имеет смысла. Он в конечном итоге обрабатывается как код sed
, и тогда s/basicConfig/setLevel/g
становится файлом для поиска.
С реализацией sed
на FreeBSD, как на macos, вы могли столкнуться с:
sed -i '' s/foo/bar/g file
Там ''
является аргументом суффикса резервной копии опции -i
, что означает, что sed
должен редактировать файл на месте без резервной копии оригинала.
Если вы уберете опцию -i
, вам также нужно убрать ''
, который идет с ней.
Ответ или решение
Вопрос о том, как использовать комбинацию команд find
, sed
и grep
для проверки замен в файлах, часто возникает среди разработчиков и системных администраторов. Давайте детально рассмотрим, как правильно организовать эти команды для достижения желаемого результата.
1. Проблема
Вы хотите использовать sed
для выполнения замены в файлах и затем проверить, была ли эта замена выполнена, с помощью grep
. Однако сложность заключается в том, что find
как команда не поддерживает прямую передачу выходных данных sed
в следующую команду, как это делается в конвейере с использованием символа |
.
2. Решение
Существует несколько способов решить эту задачу. Рассмотрим наиболее эффективные и легко запоминающиеся.
Вариант 1: Использование -exec sh -c
Этот метод позволяет выполнить несколько команд в рамках одной операции find
, передавая выходные данные sed
в grep
.
find src -type f -name "*.py" -not -name "*.pyc" -exec sh -c "sed 's/basicConfig/setLevel/g' {} | grep setLevel" \;
- Пояснение:
-exec sh -c
позволяет запустить оболочку, где мы выполняем командыsed
иgrep
.{}
— это подстановка текущего файла, найденного командойfind
.\;
завершает команду-exec
.
Вариант 2: Прямое использование -exec
Если вы хотите сразу вывести только изменённые строки без использования grep
, можно сделать так:
find src -type f -name "*.py" -not -name "*.pyc" -exec sed -n 's/basicConfig/setLevel/gp' {} +
- Пояснение:
-n
— подавляет стандартный выводsed
, аp
(print) печатает только те строки, которые были изменены.
3. Заметки о sed
Обратите внимание, что если вы используете -i
для редактирования файлов на месте, в macOS необходимо указать пустую строку после -i
, чтобы не создавать резервные копии:
find src -type f -name "*.py" -not -name "*.pyc" -exec sed -i '' 's/basicConfig/setLevel/g' {} +
- Важно: Не рекомендуется использовать опцию
-i
для тестирования, так как она изменяет оригинальный файл. Вместо этого используйте первый вариант, чтобы проверить изменения, не затрагивая исходные файлы.
4. Рекомендации по использованию
- Тестируйте команды на небольшом количестве файлов, чтобы убедиться в их правильности перед массовым запуском.
- Чистота кода: Избегайте лишних аргументов и двойных кавычек в командах
sed
, если они не нужны, чтобы не запутаться в синтаксисе. - Используйте расширенные регулярные выражения: При необходимости можно добавить флаг
-E
, чтобы активировать расширенные регулярные выражения, однако в данном случае этого не требуется, так как используются базовые выражения.
Заключение
Комбинация команд find
, sed
и grep
может показаться сложной, однако с использованием предложенных выше методов вы сможете эффективно находить и заменять текст в файлах, а также проверять изменения. Практика поможет вам лучше запомнить эти команды и их синтаксис, что повысит вашу продуктивность в работе с Linux иUnix-системами.