Bash-Befehlszeile und Eingabegrenze

88

Gibt es eine Art Zeichenbeschränkung in Bash (oder anderen Shells) für die Dauer einer Eingabe? Wenn ja, wie hoch ist diese Zeichenbeschränkung?

Dh Ist es möglich, einen Befehl in bash zu schreiben, der zu lang ist, um von der Befehlszeile ausgeführt zu werden? Wenn es kein erforderliches Limit gibt, gibt es ein vorgeschlagenes Limit?

Derek Halden
quelle
2
Das Eingabegrenzen unterscheidet sich stark vom Argumentlimit auf Betriebssystemebene (beachten Sie, dass einige andere Dinge als Argumente, wie z. B. Umgebungsvariablen, auch für dieses gelten). Der an das Betriebssystem übergebene generierte Befehl kann mehr oder weniger Zeichen enthalten als der Shell-Befehl, der ihn generiert hat.
Charles Duffy

Antworten:

123

Die Begrenzung für die Länge einer Befehlszeile wird nicht von der Shell, sondern vom Betriebssystem festgelegt. Diese Grenze liegt normalerweise im Bereich von hundert Kilobyte. POSIX bezeichnet diese Grenze ARG_MAXund auf POSIX-konformen Systemen können Sie sie abfragen

$ getconf ARG_MAX    # Get argument limit in bytes

Auf Cygwin sind dies beispielsweise 32000, und auf den verschiedenen BSDs und Linux-Systemen, die ich verwende, liegt der Wert zwischen 131072 und 2621440.

Wenn Sie eine Liste von Dateien verarbeiten müssen, die diese Grenze überschreiten, sollten Sie sich das xargsDienstprogramm ansehen , das ein Programm wiederholt mit einer Teilmenge von Argumenten aufruft, die diese Grenze nicht überschreiten ARG_MAX.

Um Ihre spezifische Frage zu beantworten, können Sie versuchen, einen Befehl mit einer zu langen Argumentliste auszuführen. Die Shell wird mit einer Meldung entlang "Argumentliste zu lang" fehlerhaft angezeigt.

Beachten Sie, dass die Eingabe in ein Programm (wie auf stdin oder einem anderen Dateideskriptor gelesen) nicht beschränkt ist (nur durch verfügbare Programmressourcen). Wenn Ihr Shell-Skript also eine Zeichenfolge in eine Variable einliest, sind Sie nicht durch eingeschränkt ARG_MAX. Die Einschränkung gilt auch nicht für Shell-Builtins.

Jens
quelle
Gute Antwort, aber ich hätte gerne eine Klarstellung. Wenn ich ein Konstrukt bekommen würde cmd <<< "$LONG_VAR"und der LONG_VAR-Wert das Limit überschreiten würde, würde es meinen Befehl sprengen?
Krzysztof Jabłoński
1
@ KrzysztofJabłoński Unwahrscheinlich, da der Inhalt von LONG_VARstdin weitergegeben wird - und das ganz in der Shell; Es wird nicht als Argument für erweitert cmd, sodass das ARG_MAX-Limit für fork () / exec () nicht ins Spiel kommt. Es ist ganz einfach, es selbst zu versuchen: Erstellen Sie eine Variable mit Inhalten, die ARG_MAX überschreiten, und führen Sie Ihren Befehl aus.
Jens
2
Hier ist die Klarstellung für die Aufzeichnung: Für eine 8-Megabyte-m4a-Datei habe ich : blah="$(cat /home/schwager/Music/Recordings/20090420\ 131623.m4a)"; cat <<< $blah >/dev/null. Beachten Sie keinen Fehler.
Mike S
3
Kleine Einschränkung. Die Umgebungsvariablen zählen ebenfalls. sysconf manpage > Es ist schwierig, ARG_MAX zu verwenden, da nicht angegeben ist, wie viel> des Argumentraums für exec (3) von den> Umgebungsvariablen des Benutzers belegt wird.
Gerrit
3
@ user188737 Ich denke, es ist eine ziemlich große Einschränkung in BUGS . Unter xargsmacOS 10.12.6 wird beispielsweise begrenzt, wie viel versucht wird, in eins exec()zu stecken ARG_MAX - 4096. Die Verwendung von Skripten xargskönnte also funktionieren, bis eines Tages jemand zu viel Material in die Umgebung bringt. Laufen Sie jetzt darauf (umgehen Sie es mit :) xargs -s ???.
Neuralmer
43

Ok, Bürger. Daher habe ich die Grenzwerte für die Befehlszeilenlänge seit einiger Zeit als Evangelium akzeptiert. Was tun mit den eigenen Annahmen? Überprüfen Sie sie natürlich.

Ich habe eine Fedora 22-Maschine zur Verfügung (dh: Linux mit bash4). Ich habe ein Verzeichnis mit 500.000 Inodes (Dateien) mit jeweils 18 Zeichen erstellt. Die Befehlszeilenlänge beträgt 9.500.000 Zeichen. So erstellt:

seq 1 500000 | while read digit; do
    touch $(printf "abigfilename%06d\n" $digit);
done

Und wir stellen fest:

$ getconf ARG_MAX
2097152

Beachten Sie jedoch, dass ich dies tun kann:

$ echo * > /dev/null

Dies schlägt jedoch fehl:

$ /bin/echo * > /dev/null
bash: /bin/echo: Argument list too long

Ich kann eine for-Schleife ausführen:

$ for f in *; do :; done

Das ist eine andere Shell gebaut.

Sorgfältiges Lesen der Dokumentation fürARG_MAX Zustände, Maximale Argumentationsdauer für die exec-Funktionen . Das heißt: Ohne Anruf execgibt es keine ARG_MAXEinschränkung. Es würde also erklären, warum Shell-Buildins nicht durch eingeschränkt werden ARG_MAX.

Und tatsächlich kann ich lsmein Verzeichnis, wenn meine Argumentliste 109948 Dateien lang ist oder ungefähr 2.089.000 Zeichen (Geben oder Nehmen). Sobald ich jedoch eine weitere 18-stellige Dateinamensdatei hinzufüge, wird eine zu lange Fehlerliste angezeigt. So ARG_MAXfunktioniert wie in der Werbung: die exec mit mehr versagt als ARG_MAXZeichen auf dem Argumente Listen- einschließlich, sollte darauf hingewiesen werden, die Umgebungsdaten.

Mike S.
quelle
Hmm. Ich hatte die vorhandene Antwort nicht gelesen, um zu implizieren, dass Buildins der fraglichen Einschränkung unterliegen, kann aber mit Sicherheit sehen, wie jemand dies könnte.
Charles Duffy
6
Ja, ich denke, es ist schwierig, sich - insbesondere für neuere Befehlszeilen-Afficianados - daran zu erinnern, dass die Situation beim Aufrufen eines eingebauten Bash im Vergleich zum Verzweigen / Ausführen eines Befehls auf nicht offensichtliche Weise anders ist. Ich wollte das klarstellen. Eine Frage, die ich in einem Vorstellungsgespräch (als Linux-Systemadministrator) immer bekomme, lautet: "Ich habe also eine Reihe von Dateien in einem Verzeichnis. Wie kann ich sie alle durchlaufen ..." Der Fragesteller fährt immer auf die Linie zu Längenbegrenzung und möchte eine find / while- oder xargs-Lösung. In Zukunft werde ich sagen: "Ah, verdammt - benutze einfach eine for-Schleife. Sie kann damit umgehen!" :-)
Mike S
@MikeS Während Sie eine for-Schleife ausführen könnten, werden Sie viel weniger und schneller sein, wenn Sie eine find-xargs-Kombination verwenden können. ;-)
Lester Cheung
4
@LesterCheung gabelt for f in *; do echo $f; donesich überhaupt nicht (alle eingebauten). Ich weiß also nicht, dass eine Find-Xargs-Kombination schneller sein wird. es wurde nicht getestet. In der Tat weiß ich nicht, was das Problem des OP ist. Vielleicht find /path/to/directoryist es für ihn nicht nützlich, weil es den Pfadnamen der Datei zurückgibt. Vielleicht mag er die Einfachheit einer for f in *Schleife. Unabhängig davon geht es im Gespräch um die Leitungseingangsgrenze und nicht um die Effizienz. Bleiben wir also beim Thema, das sich auf die Länge der Befehlszeile bezieht.
Mike S
FWIW, das Problem war, wie ich mich erinnere, nur der Versuch, eine Shell in C zu schreiben und zu bestimmen, wie lange ich Eingaben zulassen sollte.
Derek Halden
-3

Es gibt eine Puffergrenze von etwa 1024. Der Lesevorgang hängt einfach beim Einfügen oder bei der Eingabe. Um dies zu lösen, verwenden Sie die Option -e.

http://linuxcommand.org/lc3_man_pages/readh.html

-e Verwenden Sie Readline, um die Zeile in einer interaktiven Shell abzurufen

Ändern Sie Ihren Lesevorgang in "-e" und der störende Zeileneingang verschwindet.

Paul Kenjora
quelle
1
Hier geht es nicht um read: "Ist es möglich, einen Befehl in Bash zu schreiben, der zu lang ist, um von der Befehlszeile ausgeführt zu werden?"
Chai T. Rex
@ ChaiT.Rex Sie sind irgendwie korrekt, aber hier ist die Sache: Versuchen Sie, Bash interaktiv ohne Readline auszuführen, dh bash --noediting, und versuchen Sie an der neuen Eingabeaufforderung, den Befehl echo somereallylongwordauszuführen, bei dem ein längeres Wort länger als 4090 Zeichen ist. Unter Ubuntu 18.04 wurde das Wort abgeschnitten, sodass es offensichtlich etwas damit zu tun hat, dass Readline nicht aktiviert ist.
Amir