почему цикл for не вызывает ошибку «слишком длинный аргумент»?

9

Я обнаружил, что это вызовет ошибку «аргумент слишком длинный»:

ls *.*

И это не подняло бы это:

for file in *.*
do
    echo $file
done

Почему?

lamwaiman1988
источник
Вы использовали одну и ту же оболочку в обоих экспериментах? Возможно /bin/bashпротив /bin/sh(что, возможно, ссылка на тире)?
maxschlepzig
Да, я провел эксперимент с 10000 файлами с довольно длинным именем файла. «ls *» не удалось, а «для f in *» успешно.
lamwaiman1988

Ответы:

13

«Аргумент слишком долго» ошибка E2BIG, поднятый execveсистемного вызова , если общий размер аргументов (плюс среды, на некоторых системах) слишком велик. Этот execveвызов запускает внешние процессы, в частности, загружает другой исполняемый файл (есть другой вызов forkдля запуска отдельного процесса, код которого все еще находится в том же исполняемом файле). forЦикл является внутренней конструкцией оболочки, поэтому он не связан с вызовом execve. Команда ls *.*выдает ошибку не при расширении глобуса, а при lsвызове.

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

Жиль "ТАК - перестань быть злым"
источник
и почему оболочка не имеет предела?
lamwaiman1988
1
@ gunbuster363 execveПредел принудительно устанавливается ядром, он устанавливает ограничения, потому что аргументы необходимо скопировать через память ядра в одной точке, и пользовательским процессам нельзя разрешать запрашивать произвольный объем памяти оболочки. Внутри оболочки нет никаких ограничений, все, что умещается в виртуальной памяти, прекрасно.
Жиль "ТАК - перестань быть злым"
5

Я полагаю, что в первом примере lsвыполняется bashчерез системный вызов fork/ execpair, а во втором - все внутреннее bash.

execВызов имеет пределы, внутренняя обработка bashвместо не имеет (или лучше, имеет различные ограничения , которые не имеют ничего общего с exec, возможно , объемом доступной памяти).

enzotib
источник
Вы можете найти предел execв /usr/include/linux/limits.hобычно, определяемый как ARG_MAX.
jw013
Если мы используем оболочку для цикла, как вы думаете, большой список элементов будет занимать всю оперативную память?
lamwaiman1988
Этот ответ неверен. Это не «внутреннее», просто ограничения для аргументов и команд различны.
полином
5

Потому что в случае lsэтого есть аргумент, а количество аргументов ограничено.

В случае forцикла это просто список предметов. Для этого нет пределов (насколько я знаю).

Шимон Тот
источник
Определенно есть предел для расширения оболочки. Это очень сильно зависит от того, сколько у вас оперативной памяти. Попробуйте это; моя система 4 ГБ ОЗУ обеспечивает прокладку примерно в 15,2 млн. 8-байтовых аргументов:for i in {00000001..20000000} ;do ((10#$i==1)) && break; done
Peter.O
4
@fred Я действительно не думал, что упоминание ОЗУ как предела необходимо.
Шимон Тот
2
Это может не понадобиться, но такова природа комментариев. Кто-то может найти это интересным или даже ценным.
Peter.O
@fred: на самом деле да, если бы расширение очень больших аргументов было распространенной проблемой, было бы возможно реализовать это, не сохраняя все в памяти.
Маттео