Список аргументов слишком длинный для ls

48

Я получаю следующую ошибку при попытке в ls *.txt | wc -lкаталог, который содержит много файлов:

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

Зависит ли порог этого «списка аргументов» от спецификаций дистрибутива или компьютера? Обычно я бы передавал результат такого большого результата некоторым другим командам ( wc -lнапример), поэтому меня не касаются ограничения терминала.

zahypeti
источник
6
То , что считается как разбор lsвывода «s , что это плохая идея, так что лучше избегать. Для подсчета см. Какой лучший способ подсчитать количество файлов в каталоге? , для хитрого обходного пути посмотрите, почему цикл for не вызывает ошибку «слишком длинный аргумент»? ,
Манатворк
@manatwork Да, я тоже видел эти вопросы. Просто интересно, как лучше использовать или перенаправить длинный вывод команды в более общем виде.
вы можете использовать getconf ARG_MAX, чтобы получить ограничение на большинство систем на основе Unix
Prasanth

Ответы:

49

Ваш список аргументов сообщения об ошибке слишком длинный исходит от * of ls *.txt.

Эти ограничения безопасны как для бинарных программ, так и для вашего ядра. На этой странице вы увидите больше информации об этом, а также о том, как он используется и вычисляется.

Там нет такого ограничения на размер трубы. Таким образом, вы можете просто выполнить эту команду:

find -type f -name '*.txt'  | wc -l

NB. В современном Linux странные символы в именах файлов (например, переводы строк) будут экранированы такими инструментами, как lsили find, но все равно будут отображаться из * . Если вы работаете на старом Unix, вам понадобится эта команда

find -type f -name '*.txt' -exec echo \;  | wc -l

NB2: Мне было интересно, как можно создать файл с новой строкой в ​​его имени. Это не так сложно, если вы знаете хитрость:

touch "hello
world"
Корен
источник
1
Я немного изменил его для работы в случае, когда есть имена файлов с символами новой строки в них. Вы также можете добавить a, -maxdepth 1если вы не собираетесь считать файлы в подкаталогах.
Шон Дж. Гофф,
Вам не нужно -exec echo \;.
Микель
@ ShawnJ.Goff Я проверил это. В текущей версии GNU find нет необходимости в `echo`
Coren
@Coren @Mikel - не у всех есть GNU find. На findOS X и на системах busybox, и я предполагаю, что любая система на основе BSD показывает имя файла с новой строкой в ​​нем, что может привести к путанице с количеством.
Шон Дж. Гофф,
А? wc -lсчитает новые строки. Поэтому мы хотим, чтобы в нем были новые строки.
Микель
11

Это зависит главным образом от вашей версии ядра Linux.

Вы должны увидеть ограничение для вашей системы, запустив

getconf ARG_MAX

где указано максимальное количество байтов, которое может иметь командная строка после расширения оболочкой.

В Linux <2.6.23 ограничение обычно составляет 128 КБ.

В Linux> = 2.6.25 ограничение составляет 128 КБ или 1/4 размера стека (см. ulimit -s), В зависимости от того, что больше.

Смотрите man-страницу execve (2) для всех деталей.


К сожалению, трубопровод ls *.txtне решит проблему, потому что ограничение находится в операционной системе, а не в оболочке.

Оболочка расширяется *.txt, затем пытается вызвать

exec("ls", "a.txt", "b.txt", ...)

и у вас есть столько подходящих файлов, *.txtчто вы превышаете ограничение в 128 КБ.

Вам придется сделать что-то вроде

find . -maxdepth 1 -name "*.txt" | wc -l

вместо.

(И см. Комментарии Шона Дж. Гоффа ниже об именах файлов, которые содержат переводы строк.)

Mikel
источник
Извините за неспособность поднять ответ. Нужно больше репутации. :( Спасибо всем !!
Не могли бы вы объяснить, что означает .и что -maxdepth 1в последней строке? Спасибо! : D
Гильерме Саломе
2
@ GuilhermeSalomé .означает текущий каталог, -maxdepth 1означает, что он не выглядит в подкаталогах. Это должно было соответствовать тем же файлам, что и *.txt.
Микель
9

Другой обходной путь:

ls | grep -c '\.txt$'

Несмотря на то, что lsвыводит больше данных, чем ls *.txtпроизводит (или пытается произвести), он не сталкивается с проблемой «слишком длинный аргумент», потому что вы не передаете никаких аргументов ls. Обратите внимание, что grepтребуется регулярное выражение, а не шаблон сопоставления файлов.

Вы можете использовать:

ls -U | grep -c '\.txt$'

(при условии, что ваша версия lsподдерживает эту опцию). Это говорит lsне сортировать вывод, что может сэкономить время и память - и в этом случае порядок не имеет значения, так как вы просто считаете файлы. Ресурсы, потраченные на сортировку выходных данных, обычно незначительны, но в этом случае мы уже знаем, что у вас очень большое количество *.txtфайлов.

И вам следует подумать о реорганизации ваших файлов, чтобы их не было так много в одном каталоге. Это может или не может быть осуществимо.

Кит Томпсон
источник
1

MAX_ARG_PAGES представляется параметром ядра. Использование findи xargsявляется типичной комбинацией для преодоления этого ограничения, но я не уверен, что это сработает wc.

Передача выходных данных find . -name \*\.txtв файл и подсчет строк в этом файле должны использоваться в качестве обходного пути.

Брэм
источник
Вы можете сделать что-нибудь с lsвыводом, не решите это. До тех пор, пока подстановочный знак * .txt не будет превышен лимит, произойдет сбой даже до запуска lsи генерации какого-либо вывода.
manatwork
Правда я обновил свой ответ.
Брэм
Лучше. Но чтобы сделать его заменой, lsвы должны указать, -maxdepth 1чтобы избежать рекурсивного сканирования подкаталогов.
manatwork
Извините за неспособность поднять ответ. Нужно больше репутации. :(
0

Это может быть грязно, но это работает для моих нужд и в пределах моей компетенции. Я не думаю, что это работает очень быстро, но это позволило мне продолжить мой день.

ls | grep jpg | <something>

Я получал список из 90000 jpgs и отправлял их в avconv, чтобы создать интервал времени.

Ранее я использовал ls * .jpg | avconv, прежде чем я столкнулся с этой проблемой.

Ричард Беттридж
источник