Сначала отрежьте тривиальные, но неприменимые ответы: я не могу использовать ни трюк find
+, xargs
ни его варианты (например, find
с -exec
), потому что мне нужно использовать несколько таких выражений на вызов. Я вернусь к этому в конце.
Теперь для лучшего примера давайте рассмотрим:
$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Как передать эти аргументы program
?
Просто делать это не делает трюк
$ ./program $(find -L some/dir -name \*.abc | sort)
терпит неудачу, так как program
получает следующие аргументы:
[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc
Как видно, путь с пробелом был разделен и program
рассматривает его как два разных аргумента.
Цитировать пока не работает
Кажется, что начинающие пользователи, такие как я, сталкиваются с такими проблемами, как правило, произвольно добавляют кавычки, пока они, наконец, не работают - только здесь это, кажется, не помогает ...
"$(…)"
$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Поскольку кавычки предотвращают разбиение слов, все файлы передаются как один аргумент.
Цитирование отдельных путей
Перспективный подход:
$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"
Цитаты есть, конечно. Но они больше не интерпретируются. Они просто часть струн. Так что не только они не помешали разделению слов, но и попали в аргументы!
Изменить IFS
Затем я попытался поиграть с IFS
. Я бы предпочел find
с -print0
и sort
с в -z
любом случае - чтобы у них не было проблем на «проводных путях» самих. Так почему бы не заставить слова разделить null
персонажа и иметь все это?
$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc
Таким образом, он все еще разделяется на пространство и не разделяется на null
.
Я пытался разместить IFS
назначение как в $(…)
(как показано выше), так и раньше ./program
. Также я попробовал другой синтаксис , как \0
, \x0
, \x00
и цитировал с '
и "
а также и без $
. Казалось, ничего из этого не имеет значения ...
И здесь у меня нет идей. Я попробовал еще несколько вещей, но все, казалось, сводилось к тем же проблемам, которые перечислены.
Что еще я мог сделать? Это вообще выполнимо?
Конечно, я мог бы program
принять шаблоны и выполнить сам поиск. Но это двойная работа, когда он привязан к определенному синтаксису. (Как насчет предоставления файлов, grep
например?).
Также я мог бы program
принять файл со списком путей. Затем я могу легко вывести find
выражение в некоторый временный файл и указать путь только к этому файлу. Это может поддерживаться по прямым путям, так что если у пользователя есть простой путь, он может быть предоставлен без промежуточного файла. Но это не кажется хорошим - нужно создавать дополнительные файлы и заботиться о них, не говоря уже о необходимости дополнительной реализации. (С другой стороны, это может быть спасением для случаев, когда количество файлов в качестве аргументов начинает вызывать проблемы с длиной командной строки…)
В конце позвольте мне еще раз напомнить вам, что трюки find
+ xargs
(и другие) не будут работать в моем случае. Для простоты описания я показываю только один аргумент. Но мой настоящий случай выглядит примерно так:
$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES
Таким образом, выполнение xargs
одного поиска все еще оставляет меня с тем, как иметь дело с другим ...
источник
mapfile
(или его синонимуreadarray
). Но это работает!while
циклом не очищает массив. Это означает, что если файлы не найдены, массив не определен. Хотя, если он уже определен, будут добавляться новые файлы (вместо замены старых). Кажется, что добавлениеdeclare -a ABC_FILES='()';
раньшеwhile
делает свое дело. (Пока просто добавлятьABC_FILES='()';
не надо.)< <
значит здесь? Это так же, как<<
? Я не думаю, что изменение этого слова<<
приводит к синтаксической ошибке («неожиданный токен» («»). Так что же это такое и как это работает?ABC_FILES
. Это хорошо. Но полезно также сделатьABS_ARGS
пустой массив, еслиABC_FILES
он пустой или это массив('--abc-files' "${ABC_FILES[@]}")
. Таким образом, позже я могу использовать его следующим образом:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"
и быть уверенным, что он будет работать правильно, независимо от того, какая (если есть) группа пуста. Или сформулировать это по-другому: этот способ--abc-files
(и--xyz-files
) будет предоставлен, только если за ним следует какой-то фактический путь.while read ... done < <(find blah)
это обычное перенаправление оболочки<
из специального файла, созданного PROCESS SUBSTITUTION . Это отличается от конвейера,find blah | while read ... done
поскольку конвейер запускаетwhile
цикл в подоболочке, поэтому установленные в нем переменные не сохраняются для последующих команд.Вы можете использовать IFS = newline (при условии, что в именах файлов нет новой строки), но вы должны установить его во внешней оболочке ДО замены:
С,
zsh
но неbash
вы можете использовать ноль,$'\0'
а также. Даже внутриbash
вы можете обрабатывать перевод строки, если есть один достаточно странный символ, который никогда не используется какОднако этот подход не обрабатывает дополнительный запрос, который вы сделали в комментариях к ответу @ steeldriver, чтобы опустить --afiles, если find пуст.
источник
IFS
разделиться наnull
?read -d ''
в методах steeldriver используется пустая строка, а не строка с нулевым байтом. (И параметр команды в любом случае не являетсяset -o noglob
) перед использованием этого оператора split + glob (кроме как вzsh
).$'\0'
и''
.Я не уверен, что понимаю, почему ты отказался
xargs
.Строка
--xyz-files
является лишь одним из многих аргументов, и нет причин считать ее особенной, прежде чем она будет интерпретирована вашей программой. Я думаю, что вы можете пройти черезxargs
обаfind
результата:источник
-print0
в секундуfind
. Кроме того, если идти по этому пути , я бы поставил--abc-files
какecho
и - только для последовательности..abc
файлов нет, то их тоже не должно быть--abc-files
(то же самое с.xyz
). Решение на основе массива от Steeldriver также требует дополнительной логики, но эта логика здесь тривиальна, в то время как здесь может быть не так уж тривиально разрушать главное преимущество этого решения - простоту.xargs
никогда не попытаюсь разделить аргументы и сделать несколько команд вместо одной, если только это явно не указано для аргументов-L
,--max-lines
(-l
),--max-args
(-n
) или--max-chars
(-s
). Я прав? Или есть какие-то значения по умолчанию? Поскольку моя программа не справилась бы с таким разделением правильно, и я предпочел бы не назвать его ...-print0
- исправлено, спасибо. Я не знаю ответы на все вопросы, но я согласен, что мое решение затрудняет включение дополнительной логики. Я бы, наверное, сам пошел с массивами, теперь, когда я знаю этот подход. Мой ответ был не совсем для тебя. Вы уже приняли другой ответ, и я предположил, что ваша проблема решена. Я просто хотел отметить, что вы можете передавать аргументы из нескольких источниковxargs
, что на первый взгляд было неочевидным. Вы можете рассматривать это как доказательство концепции. Теперь мы все знаем несколько разных подходов и можем сознательно выбирать то, что подходит нам в каждом конкретном случае.--abc-files
). Но вы правы - хорошо знать ваши альтернативы! Тем более, что я ошибочно подумал, что это невозможно.