Если я понимаю вашу спецификацию, вы хотите:
grep --null -l '<pattern>' directory/*.extension1 | \
xargs -n 1 -0 -I{} bash -c 'rm "$1" "${1%.*}.extension2"' -- {}
По сути, это то же самое, что описывает комментарий @triplee, за исключением того, что он безопасен для новой строки.
Что тут происходит?
grep
с --null
вернет вывод, разделенный нулями вместо новой строки. Поскольку в именах файлов могут быть новые строки, разделители с новой строкой делают невозможным безопасный анализ вывода grep
, но null не является допустимым символом в имени файла и, таким образом, является хорошим разделителем.
xargs
возьмет поток элементов, разделенных новой строкой, и выполнит заданную команду, передав как можно больше этих элементов (по одному на каждый параметр) заданной команде (или echo
, если команда не указана). Таким образом, если вы сказали:
printf 'one\ntwo three \nfour\n' | xargs echo
xargs
выполнит echo one 'two three' four
. Это небезопасно для имен файлов, потому что, опять же, имена файлов могут содержать встроенные символы новой строки.
Переключение -0
на xargs
изменяет поиск разделителя новой строки на нулевой разделитель. Это позволяет ему соответствовать выходным данным, полученным от grep --null
, и делает его безопасным для обработки списка имен файлов.
Обычно xargs
просто добавляет ввод в конец команды. Переключатель -I
на xargs
изменяет это, чтобы заменить указанную строку замены вводом. Чтобы получить представление, проведите этот эксперимент:
printf 'one\ntwo three \nfour\n' | xargs -I{} echo foo {} bar
Обратите внимание на отличие от более ранней команды printf | xargs
.
В случае моего решения я выполняю команду bash
, которой я передаю -c
. Переключатель -c
заставляет bash выполнять команды в следующем аргументе (и затем завершать работу) вместо запуска интерактивной оболочки. Следующий блок 'rm "$1" "${1%.*}.extension2"'
является первым аргументом -c
и является сценарием, который будет выполняться bash
. Любые аргументы, следующие за аргументом сценария до -c
, назначаются в качестве аргументов сценария. Это, если бы я сказал:
bash -c 'echo $0' "Hello, world"
Тогда Hello, world
будет присвоено $0
(первый аргумент скрипта) и внутри скрипта я смогу echo
вернуть его обратно.
Поскольку $0
обычно зарезервировано для имени скрипта, я передаю фиктивное значение (в данном случае --
) в качестве первого аргумента, а затем вместо второго аргумента пишу {}
— строку замены, которую я указал для xargs
. Это будет заменено на xargs
с каждым именем файла, проанализированным из вывода grep
перед выполнением bash
.
Сценарий мини-оболочки может показаться сложным, но он довольно тривиален. Во-первых, весь сценарий заключен в одинарные кавычки, чтобы вызывающая оболочка не могла его интерпретировать. Внутри скрипта я вызываю rm
и передаю ему два имени файла для удаления: аргумент $1
, который был именем файла, переданным при подстановке строки замены выше, и ${1%.*}.extension2
. Последнее является подстановкой параметра в переменной $1
. Важная часть - %.*
, которая говорит
%
Найдите соответствие с конца переменной и удалите самую короткую строку, соответствующую шаблону.
.*
Образец представляет собой один период, за которым следует что-либо.
Это эффективно удаляет расширение, если таковое имеется, из имени файла. Вы сами можете наблюдать эффект:
foo='my file.txt'
bar='this.is.a.file.txt'
baz='no extension'
printf '%s\n'"${foo%.*}" "${bar%.*}" "${baz%.*}"
Поскольку расширение было удалено, я присоединяю желаемое альтернативное расширение .extension2
к удаленному имени файла, чтобы получить альтернативное имя файла.
13.03.2012