Argumentliste zu lang für ls

48

Beim Versuch, ls *.txt | wc -lein Verzeichnis zu öffnen, das viele Dateien enthält , wird die folgende Fehlermeldung angezeigt:

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

Ist der Schwellenwert für diese "Argumentliste" von der Distribution oder der Computerspezifikation abhängig? Normalerweise würde ich das Ergebnis eines so großen Ergebnisses an andere Befehle leiten ( wc -lzum Beispiel), damit ich mich nicht um die Grenzen des Terminals kümmere.

zahypeti
quelle
6
Das zählt als Parsing ls-Ausgabe , was eine schlechte Idee ist. Vermeiden Sie es also besser. Informationen zum Zählen finden Sie unter Wie wird die Anzahl der Dateien in einem Verzeichnis am besten gezählt? Lesen Sie für eine knifflige Umgehung, warum for loop den Fehler "argument too long" nicht auslöst. .
manatwork
@manatwork Ja, ich habe diese Fragen auch gesehen. Fragen Sie sich nur, wie Sie eine lange Ausgabe eines Befehls allgemeiner verwenden oder umleiten können.
Mit getconf ARG_MAX können Sie das Limit für die meisten Unix-basierten Systeme ermitteln
Prasanth

Antworten:

49

Ihre zu lange Liste der Argumente für Fehlermeldungen stammt aus dem * von ls *.txt.

Diese Begrenzung ist eine Sicherheit sowohl für Binärprogramme als auch für Ihren Kernel. Auf dieser Seite finden Sie weitere Informationen dazu sowie zur Verwendung und Berechnung.

Es gibt keine solche Begrenzung der Rohrgröße. Sie können also einfach diesen Befehl eingeben:

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

NB: Unter modernen Linux werden seltsame Zeichen in Dateinamen (wie Zeilenumbrüche) mit Werkzeugen wie lsoder maskiert find, aber immer noch von * angezeigt . Wenn Sie mit einem alten Unix arbeiten, benötigen Sie diesen Befehl

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

NB2: Ich habe mich gefragt, wie man eine Datei mit einer neuen Zeile im Namen erstellen kann. Es ist nicht so schwer, wenn Sie den Trick kennen:

touch "hello
world"
Coren
quelle
1
Ich habe es leicht geändert, damit es funktioniert, wenn Dateinamen mit Zeilenumbrüchen enthalten sind. Sie können auch ein hinzufügen, -maxdepth 1wenn Sie nicht beabsichtigen, Dateien in Unterverzeichnissen zu zählen.
Shawn J. Goff
Das brauchst du nicht -exec echo \;.
Mikel
@ ShawnJ.Goff Ich habe es getestet. In der aktuellen Version von GNU find
Coren,
@Coren @Mikel - nicht jeder hat GNU's find. Die findauf OS X und auf busybox-basierten Systemen, und ich würde mir vorstellen, dass jedes BSD-basierte System den Dateinamen mit einem Zeilenumbruch anzeigt, der mit der Zählung zu schaffen machen würde.
Shawn J. Goff
Huh? wc -lzählt Zeilenumbrüche. Wir möchten , dass es Zeilenumbrüche gibt.
Mikel
11

Dies hängt hauptsächlich von Ihrer Version des Linux-Kernels ab.

Sie sollten in der Lage sein, das Limit für Ihr System zu sehen, indem Sie ausführen

getconf ARG_MAX

Dies gibt an, wie viele Bytes eine Befehlszeile maximal haben darf, nachdem sie von der Shell erweitert wurde.

Unter Linux <2.6.23 beträgt das Limit normalerweise 128 KB.

Unter Linux> = 2.6.25 beträgt das Limit entweder 128 KB oder 1/4 Ihrer Stapelgröße (siehe ulimit -s), je nachdem, welcher Wert größer ist.

Weitere Informationen finden Sie auf der Manpage execve (2) .


Leider kann ls *.txtdas Problem durch Piping nicht behoben werden, da das Limit im Betriebssystem und nicht in der Shell liegt.

Die Shell erweitert das *.txtund versucht dann aufzurufen

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

und Sie haben so viele passende Dateien, *.txtdass Sie das 128-KB-Limit überschreiten.

Sie müssen so etwas tun

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

stattdessen.

(Siehe auch Shawn J. Goffs Kommentare zu Dateinamen, die Zeilenumbrüche enthalten.)

Mikel
quelle
Entschuldigung, dass Sie eine Antwort nicht positiv bewerten können. Benötigen Sie mehr Ruf. :( Vielen Dank an alle !!
Können Sie erklären, was das .und was das -maxdepth 1in der letzten Zeile bedeutet? Vielen Dank! : D
Guilherme Salomé
2
@ GuilhermeSalomé .bedeutet aktuelles Verzeichnis, -maxdepth 1dh es sucht nicht in Unterverzeichnissen. Dies sollte mit den gleichen Dateien wie übereinstimmen *.txt.
Mikel
9

Eine andere Problemumgehung:

ls | grep -c '\.txt$'

Auch wenn lsmehr Output ls *.txterzeugt als produziert (oder Versuche zu produzieren), stößt es nicht auf das "Argument zu lang" -Problem, da Sie keine Argumente an übergeben ls. Beachten Sie, dass grepein regulärer Ausdruck anstelle eines Dateimusters verwendet wird.

Vielleicht möchten Sie Folgendes verwenden:

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

(vorausgesetzt, Ihre Version von lsunterstützt diese Option). Dies bedeutet, dass lsdie Ausgabe nicht sortiert werden soll, was Zeit und Speicherplatz spart. In diesem Fall spielt die Reihenfolge keine Rolle, da Sie nur Dateien zählen. Der Aufwand zum Sortieren der Ausgabe ist in der Regel gering. In diesem Fall wissen wir jedoch bereits, dass Sie über eine sehr große Anzahl von *.txtDateien verfügen .

Und Sie sollten überlegen, Ihre Dateien neu zu organisieren, damit Sie nicht so viele Dateien in einem einzigen Verzeichnis haben. Dies kann möglich oder nicht möglich sein.

Keith Thompson
quelle
1

MAX_ARG_PAGES scheint ein Kernel-Parameter zu sein. Die Verwendung von findund xargsist eine typische Kombination, um dieses Limit zu erreichen, aber ich bin nicht sicher, ob es funktionieren wird wc.

Das Weiterleiten der Ausgabe find . -name \*\.txtan eine Datei und das Zählen der Zeilen in dieser Datei sollte als Problemumgehung dienen.

Bram
quelle
Sie können alles mit lsder Ausgabe tun , wird dies nicht lösen. Solange der Platzhalter * .txt über das Limit hinaus erweitert ist, schlägt dies fehl, bevor überhaupt lseine Ausgabe gestartet und generiert wird.
manatwork
Richtig, ich habe meine Antwort aktualisiert.
Bram
Besser. Um es jedoch zu einem Ersatz für zu machen, lssollten Sie angeben -maxdepth 1, dass die Unterverzeichnisse nicht rekursiv durchsucht werden sollen.
manatwork
Entschuldigung, dass Sie eine Antwort nicht positiv bewerten können. Benötigen Sie mehr Ruf. :(
0

Das mag schmutzig sein, aber es entspricht meinen Bedürfnissen und meiner Kompetenz. Ich glaube nicht, dass es sehr schnell geht, aber es hat mir erlaubt, meinen Tag fortzusetzen.

ls | grep jpg | <something>

Ich habe eine 90.000-fache Liste von JPEG-Dateien abgerufen und sie an avconv weitergeleitet, um einen Zeitraffer zu generieren.

Ich habe zuvor ls * .jpg | verwendet avconv bevor ich auf dieses Problem stieß.

Richard Bettridge
quelle