Warum löst for loop keinen Fehler "Argument zu lang" aus?

9

Ich fand, dass dies den Fehler "Argument zu lang" auslösen würde:

ls *.*

Und das würde es nicht erhöhen:

for file in *.*
do
    echo $file
done

Warum?

lamwaiman1988
quelle
Haben Sie in beiden Experimenten dieselbe Schale verwendet? Vielleicht /bin/bashvs. /bin/sh(was ist vielleicht ein Link zum Bindestrich)?
Maxschlepzig
Ja, ich habe das Experiment mit 10000 Dateien mit Dateinamen durchgeführt, die ziemlich lang sind. "ls *" ist fehlgeschlagen und "for f in *" war erfolgreich.
Lamwaiman1988

Antworten:

13

Der Fehler "Argument zu lang" wird E2BIGvom execveSystemaufruf ausgelöst, wenn die Gesamtgröße der Argumente (plus der Umgebung auf einigen Systemen) zu groß ist. Der execveAufruf ist derjenige, der externe Prozesse startet, insbesondere das Laden einer anderen ausführbaren Datei (es gibt einen anderen Aufruf fork, um einen separaten Prozess auszuführen, dessen Code immer noch aus derselben ausführbaren Datei stammt). Die forSchleife ist ein internes Shell-Konstrukt und beinhaltet keinen Aufruf execve. Der Befehl ls *.*löst den Fehler nicht aus, wenn der Glob erweitert wird, sondern wenn er lsaufgerufen wird.

execveschlägt mit dem Fehler fehl, E2BIGwenn die Gesamtgröße der Argumente für den Befehl größer als das ARG_MAX Limit ist . Mit dem Befehl können Sie den Wert dieses Grenzwerts auf Ihrem System anzeigen getconf ARG_MAX. (Es ist möglich, dass Sie diese Grenze überschreiten können, wenn Sie über genügend Speicher verfügen. Halten Sie die ARG_MAXGarantien ein, execvedie funktionieren, solange kein Fehler auftritt.)

Gilles 'SO - hör auf böse zu sein'
quelle
und warum hatte Shell kein Limit?
Lamwaiman1988
1
@ gunbuster363 Das execveLimit wird vom Kernel erzwungen, es setzt Limits, da die Argumente an einer Stelle über den Kernelspeicher kopiert werden müssen und Benutzerprozesse keine beliebige Menge an Shell-Speicher anfordern dürfen. Innerhalb der Shell gibt es keinen Grund, ein Limit zu haben. Alles, was in den virtuellen Speicher passt, ist in Ordnung.
Gilles 'SO - hör auf böse zu sein'
5

Ich nehme an, dass im ersten Beispiel über einen / pair-Systemaufruf ausgeführt wird, im zweiten Beispiel lsist die gesamte Arbeit intern .bashforkexecbash

Der execAufruf hat Grenzen, die interne Arbeitsweise bashstattdessen nicht (oder besser gesagt, hat verschiedene Grenzen, die nichts damit zu tun haben exec, vielleicht die Menge des verfügbaren Speichers).

Enzotib
quelle
Sie finden das Limit execin der /usr/include/linux/limits.hRegel definiert als ARG_MAX.
jw013
Glauben Sie, dass eine große Liste von Elementen den gesamten Arbeitsspeicher belegt, wenn wir die Shell for-Schleife verwenden?
Lamwaiman1988
Diese Antwort ist falsch. Es ist nicht 'intern', es ist nur so, dass die Grenzen für Argumente und Befehle unterschiedlich sind.
Polynom
5

Denn im Falle lsist es ein Argument, und die Anzahl der Argumente ist begrenzt.

Im Falle des forZyklus handelt es sich nur um eine Liste von Elementen. Dafür gibt es (soweit mir bekannt ist) keine Grenzen.

Šimon Tóth
quelle
Es gibt definitiv eine Grenze für die Shell-Erweiterung. Es hängt sehr stark davon ab, wie viel RAM Ihnen zur Verfügung steht. Versuchen Sie dies; Mein 4 GB RAM-System bläst eine Dichtung bei etwa 15,2 Millionen 8-Byte-Argumenten:for i in {00000001..20000000} ;do ((10#$i==1)) && break; done
Peter.O
4
@fred Ich dachte nicht wirklich, dass es notwendig ist, RAM als Limit zu erwähnen.
Šimon Tóth
2
Es wird vielleicht nicht benötigt, aber das ist die Natur von Kommentaren. Jemand mag es interessant oder sogar von Wert finden.
Peter.O
@fred: Eigentlich ja, wenn das Erweitern sehr großer Argumente ein häufiges Problem wäre, wäre es möglich, es zu implementieren, ohne alles im Speicher zu behalten.
Matteo