Если я понимаю вашу спецификацию, вы хотите:
grep --null -l '<pattern>' directory/*.extension1 | \
xargs -n 1 -0 -I{} bash -c 'rm "$1" "${1%.*}.extension2"' -- {}
По сути, это то же самое, что описывает комментарий @ triplee, за исключением того, что он безопасен для новой строки.
Что здесь происходит?
grep
с --null
вернет вывод, разделенный нулями вместо новой строки.Поскольку в именах файлов могут присутствовать символы новой строки, а разделение символом новой строки делает невозможным безопасный анализ выходных данных grep
, но значение NULL не является допустимым символом в имени файла и, таким образом, создает хороший разделитель.* примет поток элементов, разделенных символом новой строки, и выполнит заданную команду, передавая столько этих элементов (по одному на каждый параметр) данной команде (или 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
с именем удаленного файла, чтобы получить альтернативное имя файла.