«Список аргументов слишком длинный»: как мне с ним справиться, не меняя мою команду?

18

Когда я запускаю команду вроде ls */*/*/*/*.jpg, я получаю ошибку

-bash: /bin/ls: Argument list too long

Я знаю, почему это происходит: это потому, что в ядре есть ограничение на количество места для аргументов команды. Стандартный совет - изменить команду, которую я использую, чтобы не требовалось слишком много места для аргументов (например, use findand xargs).

Что если я не хочу менять команду? Что делать, если я хочу продолжать использовать ту же команду? Как я могу заставить вещи "просто работать", не получая эту ошибку? Какие решения доступны?

DW
источник
Полезное чтение: Bash FAQ 95 . Без изменения вашей команды вы ничего не сможете сделать, кроме перекомпиляции, чтобы увеличить максимальный размер списка аргументов или изменить структуру каталогов так, чтобы было меньше файлов.
jw013
1
@ jw013 в зависимости от версии ядра Linux, возможно, можно увеличить список аргументов - подробности об изменениях в последних системах см. на unix.stackexchange.com/a/45161/8979 .
Ульрих Дангел
@ UlrichDangel, да, это возможно! Смотри мой ответ; Мой ответ показывает, как это сделать (в Linux с достаточно недавним ядром).
DW

Ответы:

26

В Linux максимальный объем пространства для аргументов команды составляет 1/4 от объема доступного стекового пространства. Таким образом, решение состоит в том, чтобы увеличить объем пространства, доступного для стека.

Короткая версия: запустить что-то вроде

ulimit -s 65536

Более длинная версия: объем пространства по умолчанию, доступный для стека, составляет около 8192 КБ. Вы можете увидеть количество доступного пространства следующим образом:

$ ulimit -s
8192

Выберите большее число и установите объем пространства, доступного для стека. Например, если вы хотите попробовать выделить до 65536 КБ для стека, выполните следующее:

$ ulimit -s 65536

Возможно, вам придется поэкспериментировать с тем, насколько большим это должно быть, используя метод проб и ошибок. Во многих случаях это быстрое и грязным решение , которое устранит необходимость изменения команды и работы из синтаксиса find, xargsи т.д. (хотя я понимаю , что есть и другие преимущества для этого).

Я считаю, что это специфично для Linux. Я подозреваю, что это, вероятно, не поможет в любой другой операционной системе Unix (не проверено).

DW
источник
1
Вы можете проверить, как это работает: $ getconf ARG_MAX 2097152 $ ulimit -s 65535 $ getconf ARG_MAX 16776960
Алекс
2

Эта статья в журнале Linux предлагает 4 решения. Только четвертое решение не предполагает изменения команды:

Метод № 4 предусматривает ручное увеличение количества страниц, выделяемых в ядре для аргументов командной строки. Если вы посмотрите на файл include / linux / binfmts.h, вы найдете следующее в верхней части:

/*
 * MAX_ARG_PAGES defines the number of pages allocated for   arguments
 * and envelope for the new program. 32 should suffice, this gives
 * a maximum env+arg of 128kB w/4KB pages!
 */
#define MAX_ARG_PAGES 32

Чтобы увеличить объем памяти, выделенный для аргументов командной строки, вам просто нужно предоставить значению MAX_ARG_PAGES большее число. Как только это изменение сохранено, просто перекомпилируйте, установите и перезагрузите новое ядро, как обычно.

На моей собственной тестовой системе мне удалось решить все мои проблемы, подняв это значение до 64. После обширного тестирования у меня не возникло ни одной проблемы с момента переключения. Это вполне ожидаемо, поскольку даже при MAX_ARG_PAGESзначении 64 максимально длинная командная строка, которую я мог бы создать, занимала бы только 256 КБ системной памяти - не очень по современным стандартам системного оборудования.

Преимущества метода № 4 очевидны. Теперь вы можете просто запустить команду, как обычно, и она успешно завершается. Недостатки одинаково очевидны. Если вы увеличите объем памяти, доступный для командной строки, за пределы объема доступной системной памяти, вы можете создать DOS-атаку на вашу собственную систему и вызвать ее сбой. В частности, в многопользовательских системах даже небольшое увеличение может оказать существенное влияние, поскольку каждому пользователю затем выделяется дополнительная память. Поэтому всегда проводите тщательное тестирование в своей среде, так как это самый безопасный способ определить, является ли метод №4 приемлемым для вас.

Я согласен, что ограничение серьезно раздражает.

Франк Дернонкур
источник
1

Вместо того ls */*/*/*/*.jpg, чтобы попробовать:

echo */*/*/*/*.jpg | xargs ls

xargs(1) знает, каково максимальное количество аргументов в системе, и будет разбивать ее стандартный ввод для многократного вызова указанной командной строки без аргументов, превышающих это ограничение, независимо от того, какое оно есть (вы также можете установить его ниже, чем максимальная ОС, используя -nопцию).

Например, предположим, ограничение составляет 3 аргумента, и у вас есть пять файлов. В этом случае xargsбудет выполнено lsдважды:

  1. ls 1.jpg 2.jpg 3.jpg
  2. ls 4.jpg 5.jpg

Часто это идеально подходит, но не всегда - например, вы не можете полагаться на ls(1) правильную сортировку всех записей для вас, потому что каждый отдельный ls-invocation будет сортировать только подмножество записей, данных ему xargs.

Хотя вы можете увеличить лимит, как это предлагают другие, он все равно будет существовать, и однажды ваша JPG-коллекция снова перерастет его. Вы должны подготовить свои сценарии для работы с бесконечным числом ...

Михаил Т.
источник
Спасибо за идею! Это не плохой обходной путь. Два предостережения: 1. Это разбивает каталоги и имена файлов, в имени которых есть пробелы, поэтому это не идеальная замена. 2. Это будет работать в том же номере с Argument list too long, но echoвместо того ls, на снарядах , где echoне оболочка встроенной команды? (Может быть, это не проблема в большинстве оболочек, так что, возможно, это не имеет значения.)
DW
1
Да, специальные символы в именах файлов являются проблемой. Лучше всего использовать findс -print0предикатом - и труба его выход в xargsс -0опцией. echoявляется встроенной оболочкой и не страдает от ограничения командной строки exec(3).
Михаил Т.