Я пытаюсь вызвать скрипт со списком имен файлов, собранных find
. Ничего особенного, просто что-то вроде этого:
$ myscript `find . -name something.txt`
Проблема в том, что некоторые пути содержат пробелы, поэтому при раскрытии аргумента они разбиваются на два недопустимых имени. Обычно я бы заключал имена в кавычки, но здесь они вставляются расширением обратной цитаты. Я попытался отфильтровать вывод find
и окружить каждое имя файла кавычками, но к тому времени, когда bash их видит, уже слишком поздно их удалять, и они рассматриваются как часть имени файла:
$ myscript `find . -name something.txt | sed 's/.*/"&"/'`
No such file or directory: '"./somedir/something.txt"'
Да, это правила обработки командной строки, но как мне обойти это?
Это смущает, но я не могу придумать правильный подход. Я наконец-то понял, как это сделать xargs -0 -n 10000
... но это такой уродливый хак, что я все еще хочу спросить: как я могу процитировать результаты расширения обратной цитаты или добиться того же эффекта другим способом?
Edit: я был смущен о том , что xargs
делает собрать все аргументы в один список аргументов, если это не указано иное или системные ограничения могут быть превышены. Спасибо всем за то, что поправили меня! Другие, имейте это в виду, когда читаете принятый ответ, потому что он не указан прямо.
Я принял ответ, но мой вопрос остается: не существует ли способа защитить пробелы в $(...)
расширении backtick (или )? (Обратите внимание, что принятое решение не является решением bash).
IFS="
перевод строки"
). Но нужно ли выполнять скрипт над всеми именами файлов? Если нет, рассмотрите возможность использования find для выполнения скрипта для каждого файла.Ответы:
Вы могли бы сделать следующее, используя некоторые реализации
find
и томуxargs
подобное.или, стандартно, просто
find
:пример
Скажем, у меня есть следующий образец каталога.
Теперь скажем, у меня есть это для
./myscript
.Теперь, когда я запускаю следующую команду.
Или когда я использую 2-ую форму примерно так:
Детали
найти + xargs
Вышеуказанные 2 метода, хотя и выглядят по-разному, по сути одинаковы. Первый - это получение результата от find, разделение его с помощью NULLs (
\0
) через-print0
переключатель для поиска.xargs -0
Специально разработан , чтобы принять входные данные, которые разделим с помощью NULLs. Это нестандартный синтаксис был введен GNUfind
и ,xargs
но также находится в настоящее время в некоторых других , как и большинстве последнего BSDs.-r
Опция требуется , чтобы избежать не вызываяmyscript
еслиfind
усматривает с GNU ,find
но не с BSDs.ПРИМЕЧАНИЕ. Весь этот подход основан на том факте, что вы никогда не пропустите чрезвычайно длинную строку. Если это так, то 2-й вызов
./myscript
будет запущен вместе с остальными последующими результатами поиска.найти с +
Это стандартный способ (хотя он был добавлен относительно недавно (2005 г.) к реализации GNU
find
). Способность делать то, что мы делаем,xargs
буквально встроенаfind
. Поэтомуfind
найдет список файлов, а затем передаст этому списку столько аргументов, сколько может соответствовать команде, указанной после-exec
(обратите внимание, что в этом случае это{}
может быть только последний раз+
в этом случае), выполняя команды несколько раз, если это необходимо.Почему нет цитирования?
В первом примере мы используем ярлык, полностью избегая проблем с цитированием, используя NULL для разделения аргументов. Когда
xargs
дается этот список, он получает указание на NULL, эффективно защищающие наши отдельные атомы команд.Во втором примере мы храним результаты внутри,
find
и поэтому он знает, что представляет собой каждый атом файла, и гарантирует, что обработает их должным образом, тем самым избегая целого бизнеса их цитирования.Максимальный размер командной строки?
Этот вопрос возникает время от времени, поэтому в качестве бонуса я добавляю его к этому ответу, главным образом, чтобы найти его в будущем. Вы можете использовать,
xargs
чтобы увидеть, что такое ограничение среды:источник
+
аргументеfind
(и вы+
тоже используете прозу, поэтому я пропустил ваше объяснение в первый раз). Но, что более важно, я бы неправильно понял, чтоxargs
делает по умолчанию !!! За три десятилетия использования Unix у меня никогда не было его применения до сих пор, но я думал, что знаю свой инструментарий ...xargs
, это дьявол команды. Вы должны прочитать его иfind
справочные страницы много раз, чтобы понять, что они могут сделать. Май выключателей противоречит друг другу, что добавляет путаницы.$(..)
вместо этого сейчас. Он автоматически обрабатывает вложение кавычек и т. Д. Бэктикс не рекомендуется.Выше
find
находит все совпадающие имена файлов и предоставляет их в качестве аргументовmyscript
. Это работает с именами файлов независимо от пробелов или любых других нечетных символов.Если все имена файлов помещаются в одну строку, myscript выполняется один раз. Если список слишком длинный, чтобы оболочка могла его обработать, тогда find будет запускать myscript по мере необходимости.
Еще: Сколько файлов помещается в командной строке?
man find
говорит, чтоfind
строит его командными строками "почти так же, как это делает xargs". И,man xargs
что ограничения зависят от системы, и вы можете определить их, запустивxargs --show-limits
. (getconf ARG_MAX
также возможно). В Linux ограничение обычно (но не всегда) составляет около 2 миллионов символов в командной строке.источник
Несколько дополнений к прекрасному ответу @ slm.
Ограничение размера аргументов относится к
execve(2)
системному вызову (на самом деле это кумулятивный размер аргумента, а также строк и указателей среды). Еслиmyscript
написано на языке, который ваша интерпретируемая оболочка может интерпретировать, то, возможно, вам не нужно его выполнять , вы можете сделать так, чтобы ваша оболочка интерпретировала его без необходимости выполнения другого интерпретатора.Если вы запустите скрипт как:
Это как:
За исключением того, что он интерпретируется дочерним элементом текущей оболочки, вместо того, чтобы выполнять его (что в конечном итоге подразумевает выполнение
sh
(или что-либо еще в строке she-bang, если она есть) с еще большим количеством аргументов).Очевидно, что вы не можете использовать
find -exec {} +
эту.
команду, так как.
она является встроенной командой оболочки, она должна выполняться оболочкой, а не командойfind
.С
zsh
этим легко:Или:
Хотя в первую очередь
zsh
вам это не понадобится, такfind
как большинство его функций встроено вzsh
глобализацию.bash
переменные, однако, не могут содержать NUL-символов, поэтому вам нужно найти другой путь. Одним из способов может быть:Вы также можете использовать рекурсивное сглаживание в стиле zsh с
globstar
параметром вbash
4.0 и более поздних версиях:Обратите внимание, что
**
после символических ссылок на каталоги, пока это не было исправлено вbash
4.3. Также обратите внимание, чтоbash
здесь не реализованыzsh
классификаторы глобинга, поэтому вы не сможете получить все их возможностиfind
.Другой альтернативой будет использование GNU
ls
:Приведенные выше методы могут быть использованы , если вы хотите , чтобы убедиться , что
myscript
это выполняются только один раз (неудачно , если список аргументов слишком велик). В последних версиях Linux вы можете увеличить и даже снять это ограничение в списке аргументов с помощью:(Размер стека 1 ГБ, четверть которого можно использовать для списка arg + env).
(безлимитный)
источник
В большинстве систем существует ограничение на длину командной строки, передаваемой любой программе с использованием
xargs
или-exec command {} +
. Отman find
:Призывов будет намного меньше, но не гарантированно будет один. Что вы должны сделать, это прочитать NUL разделенные имена файлов в сценарии из стандартного ввода, возможно, на основе аргумента командной строки
-o -
. Я хотел бы сделать что-то вроде:и реализовать аргументы опции
myscript
соответственно.источник
xargs
работает. Ваше решение действительно самое надежное, но в этом случае оно излишне.Нет, нет Это почему?
У Баша нет возможности узнать, что следует защищать, а что нет.
В файле / канале unix нет массивов. Это просто поток байтов. Команда внутри
``
или$()
выводит поток, который bash глотает и обрабатывает как одну строку. Таким образом, у вас есть только два варианта: поместить его в кавычки, сохранить его в виде одной строки или обнажить, чтобы bash разделял его в соответствии с настроенным поведением.Итак, что вам нужно сделать, если вы хотите, чтобы массив - это определить формат байта с массивом, и вот что инструменты любят
xargs
иfind
делают: если вы запускаете их с-0
аргументом, они работают в соответствии с форматом двоичного массива, который завершает элементы нулевой байт, добавляя семантику в непрозрачный поток байтов.К сожалению,
bash
нельзя настроить разбиение строк на нулевой байт. Спасибо /unix//a/110108/17980 за то, что показали нам, чтоzsh
можете.xargs
Вы хотите, чтобы ваша команда запускалась один раз, и вы сказали, что это
xargs -0 -n 10000
решает вашу проблему. Это не так, это гарантирует, что если у вас более 10000 параметров, ваша команда будет выполняться более одного раза.Если вы хотите, чтобы он строго выполнялся один раз или не выполнялся, вы должны предоставить
-x
аргумент и-n
аргумент, больший, чем-s
аргумент (действительно: достаточно большой, чтобы целый набор аргументов нулевой длины плюс имя команды не помещались в-s
размер). ( человек xargs , см. выдержку далеко ниже)Система, в которой я сейчас работаю, имеет размер стека, ограниченный примерно 8М, поэтому вот мой предел:
удар
Если вы не хотите задействовать внешнюю команду, цикл while-read, питающий массив, как показано в /unix//a/110108/17980 , является единственным способом для bash разделить объекты в нулевой байт.
Идея создать скрипт,
( . ... "$@" )
чтобы избежать ограничения размера стека, крутая (я пробовал, она работает!), Но, вероятно, не важна для нормальных ситуаций.Использование специального fd для конвейера процесса важно, если вы хотите прочитать что-то еще из stdin, но в противном случае вам это не понадобится.
Итак, самый простой «родной» способ, для повседневных бытовых нужд:
Если вам нравится, что ваше дерево процессов чистое и приятно смотреть, этот метод позволяет вам сделать это
exec mynonscript "${files[@]}"
, удалив процесс bash из памяти, заменив его вызываемой командой.xargs
всегда будет оставаться в памяти во время выполнения вызываемой команды, даже если команда будет выполняться только один раз.Что говорит против родного метода bash, так это:
bash не оптимизирован для обработки массивов.
человек xargs :
источник
ls "what is this"
противls `echo '"what is this"'`
. Кто-то забыл внедрить обработку цитат для результата обратных цитат.$(...)
расширении backtick (или )?», Поэтому представляется целесообразным игнорировать обработку, которая не выполняется в этой ситуации.bash
который не поддерживает его изначально, как, очевидно,zsh
делает.printf "%s\0"
иxargs -0
для обхода ситуации цитирования, когда промежуточный инструмент передавал параметры через строку, анализируемую оболочкой. Цитирование всегда возвращается, чтобы укусить вас.