У вас есть множество ответов, которые хорошо объясняют, как это сделать; но для завершения я повторю и добавлю к этому:
xargs
всегда полезен только для интерактивного использования (когда вы знаете, что все ваши имена файлов просты - без пробелов и кавычек) или когда используется опция -0
. Иначе все сломается.
find
- очень полезный инструмент; использование его для передачи имен файлов в xargs
(даже с -0
) довольно запутанно, поскольку find
может делать все сам с помощью -exec command {} \;
или -exec command {} +
в зависимости от того, что вы хотите:
find /path -name 'pattern' -exec somecommand {} \;
find /path -name 'pattern' -exec somecommand {} +
Первый из них запускает somecommand
с одним аргументом для каждого файла рекурсивно в /path
, который соответствует pattern
.
Последний запускает somecommand
с столько аргументов, сколько умещается в командной строке одновременно для файлов рекурсивно в /path
, которые соответствуют pattern
.
Какой из них использовать, зависит от somecommand
. Если он может принимать несколько аргументов имени файла (например, rm
, grep
и т. Д.), Тогда последний вариант работает быстрее (поскольку вы запускаете somecommand
гораздо реже). Если somecommand
принимает только один аргумент, вам нужно первое решение. Посмотрите на справочную страницу somecommand
.
Подробнее о find
: http://mywiki.wooledge.org/UsingFind
В bash
, for
- это оператор, который перебирает аргументов . Если вы делаете что-то вроде этого:
for foo in "$bar"
вы даете for
один аргумент для итерации (обратите внимание на кавычки!). Если вы делаете что-то вроде этого:
for foo in $bar
вы просите bash
взять содержимое bar
и разорвать его на части, где есть пробелы, символы табуляции или новые строки (технически, какие бы символы не были в IFS
), и использовать фрагменты этой операции в качестве аргументов. для. Это НЕ имена файлов . Предполагать, что результат разрывания длинной строки, которая содержит имена файлов, где есть пробелы в куче имен файлов, просто неверен. Как вы только что заметили.
Ответ таков: не используйте for
, это, очевидно, неправильный инструмент. Все вышеприведенные команды find
предполагают, что somecommand
является исполняемым файлом в PATH
. Если это оператор bash
, вам понадобится эта конструкция (перебирает вывод find
, как вы пытались, но безопасно):
while read -r -d ''; do
somebashstatement "$REPLY"
done < <(find /path -name 'pattern' -print0)
При этом используется цикл while-read
, который считывает части строки, выводимой find
, до тех пор, пока не достигнет байта NULL
(то, что -print0
использует для разделения имен файлов). Поскольку NULL
байтов не могут быть частью имен файлов (в отличие от пробелов, табуляции и новых строк), это безопасная операция.
Если вам не нужно, чтобы somebashstatement
был частью вашего скрипта (например, он не меняет среду скрипта, сохраняя счетчик или устанавливая переменную или что-то подобное), тогда вы все равно можете использовать find
' s -exec
для запуска вашего оператора bash
:
find /path -name 'pattern' -exec bash -c 'somebashstatement "$1"' -- {} \;
find /path -name 'pattern' -exec bash -c 'for file; do somebashstatement "$file"; done' -- {} +
Здесь -exec
выполняет команду bash
с тремя или более аргументами.
- Оператор bash для выполнения.
- A
--
. bash
поместит это в $0
, вы можете поместить все, что вам нравится, на самом деле.
- Ваше имя файла или имена файлов (в зависимости от того, использовали ли вы
{} \;
или {} +
соответственно). Имя файла заканчивается в $1
(и $2
, $3
, ... если их больше одного, конечно).
Оператор bash
в первой команде find
здесь выполняет somebashstatement
с именем файла в качестве аргумента.
Оператор bash
во второй команде find
здесь выполняет цикл for
(! ), который выполняет итерацию по каждому позиционному параметру (вот что такое сокращенный синтаксис for
- for foo; do
- делает) и запускает somebashstatement
с именем файла в качестве аргумента. Разница между самым первым оператором find
, который я показал с -exec {} +
, заключается в том, что мы запускаем только один bash
процесс для большого количества имен файлов, но по-прежнему один somebashstatement
для каждого из этих имен файлов.
Все это также хорошо объяснено на странице UsingFind
, ссылки на которую приведены выше.