"Argumentliste zu lang": Wie gehe ich damit um, ohne meinen Befehl zu ändern?

18

Wenn ich einen Befehl wie ausführen ls */*/*/*/*.jpg, wird der Fehler angezeigt

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

Ich weiß, warum dies passiert: Es liegt daran, dass der Platz für Argumente zu einem Befehl im Kernel begrenzt ist. Die Standardempfehlung lautet, den von mir verwendeten Befehl zu ändern, um zu vermeiden, dass so viel Platz für Argumente benötigt wird (z. B. findund xargs).

Was ist, wenn ich den Befehl nicht ändern möchte? Was ist, wenn ich weiterhin denselben Befehl verwenden möchte? Wie kann ich Dinge "einfach funktionieren" lassen, ohne diesen Fehler zu bekommen? Welche Lösungen gibt es?

DW
quelle
Nützliche Lektüre: Bash FAQ 95 . Ohne den Befehl zu ändern, können Sie nur eine Neukompilierung durchführen, um die maximale Größe der Argumentliste zu erhöhen oder die Verzeichnisstruktur so zu ändern, dass weniger Dateien vorhanden sind.
JW013
1
@ jw013 Basierend auf der Linux-Kernel-Version kann die Liste der Argumente möglicherweise erweitert werden. Weitere Informationen zu den Änderungen in neueren Systemen finden Sie unter unix.stackexchange.com/a/45161/8979 .
Ulrich Dangel
@UlrichDangel, Yup, es ist möglich! Siehe meine Antwort; Meine Antwort zeigt, wie es geht (unter Linux, mit einem Kernel, der aktuell genug ist).
DW

Antworten:

26

Unter Linux beträgt der maximale Speicherplatz für Befehlsargumente 1/4 des verfügbaren Stapelspeichers. Eine Lösung besteht darin, den für den Stapel verfügbaren Platz zu vergrößern.

Kurzversion: Führen Sie so etwas wie

ulimit -s 65536

Längere Version: Der für den Stapel standardmäßig verfügbare Speicherplatz beträgt etwa 8192 KB. Sie können den verfügbaren Speicherplatz wie folgt anzeigen:

$ ulimit -s
8192

Wählen Sie eine größere Anzahl und legen Sie den verfügbaren Speicherplatz für den Stapel fest. Wenn Sie beispielsweise versuchen möchten, bis zu 65536 KB für den Stapel zuzulassen, führen Sie Folgendes aus:

$ ulimit -s 65536

Möglicherweise müssen Sie mit Trial-and-Error-Methoden herumspielen, wie groß diese sein müssen. In vielen Fällen ist dies eine schnelle und unsaubere Lösung , die die Notwendigkeit zu beseitigen , den Befehl und Arbeit aus der Syntax zu ändern find, xargsusw. (obwohl ich weiß , es gibt noch andere Vorteile zu tun , so).

Ich glaube, dass dies Linux-spezifisch ist. Ich vermute, es wird wahrscheinlich auf keinem anderen Unix-Betriebssystem (nicht getestet) helfen.

DW
quelle
1
Sie können so überprüfen, ob es funktioniert hat: $ getconf ARG_MAX 2097152 $ ulimit -s 65535 $ getconf ARG_MAX 16776960
Alex
2

Dieser Artikel im Linux Journal enthält 4 Lösungen. Nur die vierte Lösung beinhaltet keine Änderung des Befehls:

Bei Methode 4 wird die Anzahl der Seiten, die im Kernel für Befehlszeilenargumente zugeordnet sind, manuell erhöht. Wenn Sie sich die Datei include / linux / binfmts.h ansehen, finden Sie im oberen Bereich Folgendes:

/*
 * 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

Um den für die Befehlszeilenargumente reservierten Speicherplatz zu erhöhen, müssen Sie lediglich den Wert MAX_ARG_PAGES mit einer höheren Zahl versehen. Sobald diese Änderung gespeichert ist, kompilieren Sie den neuen Kernel erneut, installieren Sie ihn und starten Sie ihn neu, wie Sie es normalerweise tun würden.

Auf meinem eigenen Testsystem konnte ich alle meine Probleme lösen, indem ich diesen Wert auf 64 erhöhte. Nach ausgiebigen Tests habe ich seit dem Wechsel kein einziges Problem mehr festgestellt. Dies ist völlig zu erwarten, da selbst mit MAX_ARG_PAGES64 die längste mögliche Befehlszeile, die ich erzeugen könnte, nur 256 KB Systemspeicher belegen würde - nach heutigen Systemhardwarestandards nicht sehr viel.

Die Vorteile von Methode 4 liegen auf der Hand. Sie können den Befehl jetzt einfach wie gewohnt ausführen und er wird erfolgreich ausgeführt. Die Nachteile liegen auf der Hand. Wenn Sie den für die Befehlszeile verfügbaren Speicher über den verfügbaren Systemspeicher hinaus erhöhen, können Sie einen DOS-Angriff auf Ihr eigenes System ausführen und zum Absturz bringen. Insbesondere bei Mehrbenutzersystemen kann eine geringfügige Erhöhung erhebliche Auswirkungen haben, da dann jedem Benutzer der zusätzliche Arbeitsspeicher zugewiesen wird. Testen Sie daher immer ausgiebig in Ihrer eigenen Umgebung, da dies die sicherste Methode ist, um festzustellen, ob Methode 4 für Sie in Frage kommt.

Ich bin damit einverstanden, dass die Einschränkung sehr ärgerlich ist.

Franck Dernoncourt
quelle
1

ls */*/*/*/*.jpgVersuchen Sie stattdessen :

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

xargs(1) weiß, wie viele Argumente sich maximal im System befinden, und zerlegt die Standardeingabe, um die angegebene Befehlszeile mehrmals aufzurufen. Dabei darf diese Grenze nicht überschritten werden. Sie können sie auch niedriger als festlegen das Maximum des Betriebssystems unter Verwendung der -nOption).

Angenommen, das Limit liegt bei 3 Argumenten und Sie haben fünf Dateien. In diesem Fall xargswird lszweimal ausgeführt:

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

Oft ist dies perfekt geeignet, aber nicht immer - Sie können sich beispielsweise nicht darauf verlassen, dass ls(1) alle Einträge ordnungsgemäß für Sie sortiert werden, da bei jedem separaten lsAufruf nur die Teilmenge der Einträge sortiert wird, nach denen sie sortiert wurden xargs.

Obwohl Sie das von anderen vorgeschlagene Limit überschreiten können, wird es immer noch ein Limit geben - und eines Tages wird Ihre JPG-Sammlung wieder größer. Sie sollten Ihre Skripte darauf vorbereiten, mit einer unendlichen Zahl umzugehen ...

Mikhail T.
quelle
Danke für die Idee! Dies ist keine schlechte Problemumgehung. Zwei Vorsichtsmaßnahmen: 1. Dies unterbricht Verzeichnisse und Dateinamen, deren Name Leerzeichen enthält, sodass dies kein perfekter Ersatz ist. 2. Ist das mit in die gleiche Ausgabe laufen gehen Argument list too long, aber für echostatt ls, auf Schalen , wo echonicht eine Shell ist eingebauter Befehl? (Vielleicht ist das in den meisten Shells kein Problem, also ist das vielleicht irrelevant.)
DW
1
Ja, Sonderzeichen in Dateinamen sind ein Problem. Am besten verwenden Sie finddas -print0Prädikat und leiten die Ausgabe xargsmit der -0Option ein. echoist eine eingebaute Shell und unterliegt nicht der Befehlszeilenbeschränkung von exec(3).
Mikhail T.