Есть 3 ключевых момента, которые следует иметь в виду при возникновении Argument list too long
ошибки:
Длина аргументов командной строки ограничена ARG_MAX
переменной, которая по определению POSIX равна "... [m] максимальная длина аргумента для функций exec, включая данные среды" (выделение добавлено) ". То есть, когда оболочка выполняет не Команда -built-it, она должна вызывать одну из них, чтобы вызвать exec()
процесс этой команды, и именно здесь ARG_MAX
вступает в игру. Кроме того, /bin/echo
играет роль имя или путь к самой команде (например, ).
Встроенные команды оболочки выполняются оболочкой, что означает, что оболочка не использует exec()
семейство функций и поэтому не подвержена влиянию ARG_MAX
переменных.
Некоторые команды, такие как xargs
и find
знают о ARG_MAX
переменной, и многократно выполняют действия в рамках этого ограничения
Из вышеприведенных пунктов и, как показано в превосходном ответе Кусалананды на связанный вопрос, это Argument list too long
также может произойти, когда обстановка велика. Таким образом, принимая во внимание, что среда каждого пользователя может варьироваться, и размер аргумента в байтах является релевантным, трудно придумать одно количество файлов / аргументов.
Как справиться с такой ошибкой?
Главное - сосредоточиться не на количестве файлов, а на том, включает ли команда, которую вы собираетесь использовать, exec()
семейство функций и тангенциально - пространство стека.
Использовать встроенные модули оболочки
Как уже говорилось ранее, встроенные функции оболочки защищены от ARG_MAX
ограничений, таких как for
циклы, while
циклы, встроенные echo
и встроенные функции printf
- все они будут работать достаточно хорошо.
for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done
На связанный вопрос об удалении файлов было решение как таковое:
printf '%s\0' *.jpg | xargs -0 rm --
Обратите внимание, что здесь используется встроенная оболочка printf
. Если мы вызываем внешнее printf
, это будет связано exec()
, следовательно, потерпит неудачу с большим количеством аргументов:
$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long
массивы bash
Согласно ответу по jlliagre, bash
не накладывают ограничения на массивах, поэтому строить массив имен файлов и использования срезов каждой итерации петли можно сделать так, как показано на danjpreron в ответе :
files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do
cp -t /path/to/new_dir/ "${files[@]:I:1000}"
done
Это, однако, имеет ограничение быть специфичным для bash и не относящимся к POSIX.
Увеличить пространство стека
Иногда вы можете видеть , что люди предполагают увеличение пространства стека с ulimit -s <NUM>
; в Linux значение ARG_MAX составляет 1/4 стекового пространства для каждой программы, что означает, что увеличение стекового пространства пропорционально увеличивает пространство для аргументов.
# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $(( $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304
Согласно ответу Franck Dernoncourt , который цитирует Linux Journal, можно также перекомпилировать ядро Linux с большим значением для максимального количества страниц памяти для аргументов, однако это больше работы, чем необходимо, и открывает потенциал для эксплойтов, как указано в цитируемой статье Linux Journal.
Избегайте раковины
Другой способ, это использовать python
или python3
которые поставляются по умолчанию с Ubuntu. Пример python + here-doc, приведенный ниже, - это то, что я лично использовал для копирования большого каталога файлов где-то в диапазоне 40000 элементов:
$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
> if os.path.isfile(f):
> shutil.copy(f,'./newdir/')
> EOF
Для рекурсивных обходов вы можете использовать os.walk .
Смотрите также: