Майте на увазі 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
або python3
Ubuntu за замовчуванням. Приклад 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 .
Дивись також: