Разве нет способа защитить пробелы в расширении backtick (или $ (...))?
Нет, нет Это почему?
У Баша нет возможности узнать, что следует защищать, а что нет.
В файле / канале unix нет массивов. Это просто поток байтов. Команда внутри ``
или $()
выводит поток, который bash глотает и обрабатывает как одну строку. Таким образом, у вас есть только два варианта: поместить его в кавычки, сохранить его в виде одной строки или обнажить, чтобы bash разделял его в соответствии с настроенным поведением.
Итак, что вам нужно сделать, если вы хотите, чтобы массив - это определить формат байта с массивом, и вот что инструменты любят xargs
и find
делают: если вы запускаете их с -0
аргументом, они работают в соответствии с форматом двоичного массива, который завершает элементы нулевой байт, добавляя семантику в непрозрачный поток байтов.
К сожалению, bash
нельзя настроить разбиение строк на нулевой байт. Спасибо /unix//a/110108/17980 за то, что показали нам, что zsh
можете.
xargs
Вы хотите, чтобы ваша команда запускалась один раз, и вы сказали, что это xargs -0 -n 10000
решает вашу проблему. Это не так, это гарантирует, что если у вас более 10000 параметров, ваша команда будет выполняться более одного раза.
Если вы хотите, чтобы он строго выполнялся один раз или не выполнялся, вы должны предоставить -x
аргумент и -n
аргумент, больший, чем -s
аргумент (действительно: достаточно большой, чтобы целый набор аргументов нулевой длины плюс имя команды не помещались в -s
размер). ( человек xargs , см. выдержку далеко ниже)
Система, в которой я сейчас работаю, имеет размер стека, ограниченный примерно 8М, поэтому вот мой предел:
$ printf '%s\0' -- {1..1302582} | xargs -x0n 2076858 -s 2076858 /bin/true
xargs: argument list too long
$ printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true
(no output)
удар
Если вы не хотите задействовать внешнюю команду, цикл while-read, питающий массив, как показано в /unix//a/110108/17980 , является единственным способом для bash разделить объекты в нулевой байт.
Идея создать скрипт, ( . ... "$@" )
чтобы избежать ограничения размера стека, крутая (я пробовал, она работает!), Но, вероятно, не важна для нормальных ситуаций.
Использование специального fd для конвейера процесса важно, если вы хотите прочитать что-то еще из stdin, но в противном случае вам это не понадобится.
Итак, самый простой «родной» способ, для повседневных бытовых нужд:
files=()
while IFS= read -rd '' file; do
files+=("$file")
done <(find ... -print0)
myscriptornonscript "${files[@]}"
Если вам нравится, что ваше дерево процессов чистое и приятно смотреть, этот метод позволяет вам сделать это exec mynonscript "${files[@]}"
, удалив процесс bash из памяти, заменив его вызываемой командой. xargs
всегда будет оставаться в памяти во время выполнения вызываемой команды, даже если команда будет выполняться только один раз.
Что говорит против родного метода bash, так это:
$ time { printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true; }
real 0m2.014s
user 0m2.008s
sys 0m0.172s
$ time {
args=()
while IFS= read -rd '' arg; do
args+=( "$arg" )
done < <(printf '%s\0' -- $(echo {1..1302581}))
/bin/true "${args[@]}"
}
bash: /bin/true: Argument list too long
real 107m51.876s
user 107m38.532s
sys 0m7.940s
bash не оптимизирован для обработки массивов.
человек xargs :
-n max-args
Используйте не более max-args аргументов в командной строке. Если будет превышен размер (см. Параметр -s), будет использовано меньше аргументов, чем max-args, если не задана опция -x, в этом случае xargs завершится.
-s макс-чарс
Используйте не более max-chars символов в командной строке, включая команду и начальные аргументы и завершающие нули на концах строк аргументов. Максимально допустимое значение зависит от системы и рассчитывается как ограничение длины аргумента для exec, за вычетом размера вашей среды, меньше 2048 байт запаса. Если это значение больше 128 КБ, в качестве значения по умолчанию используется 128 КБ; в противном случае значение по умолчанию является максимальным. 1 КБ составляет 1024 байта.
-Икс
Выход, если размер (см. Параметр -s) превышен.
IFS="
перевод строки"
). Но нужно ли выполнять скрипт над всеми именами файлов? Если нет, рассмотрите возможность использования find для выполнения скрипта для каждого файла.