Майте на увазі 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
Відповідно до відповіді Франка Дернонкура , який цитує Linux Journal, можна також перекомпілювати ядро Linux з більшим значенням для максимальної кількості сторінок пам'яті для аргументів, однак це більше роботи, ніж потрібно, і відкриває потенціал для подвигів, як зазначено в цитованій статті Linux Journal.
Уникайте оболонок
Інший спосіб - це використання pythonабо python3Ubuntu за замовчуванням. Приклад python + here-doc, наведений нижче, - це те, що я особисто використовував для копіювання великого каталогу файлів десь у межах 40 000 елементів:
$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
> if os.path.isfile(f):
> shutil.copy(f,'./newdir/')
> EOF
Для рекурсивних обходів можна використовувати os.walk .
Дивись також: