Fehler 'Argumentliste zu lang' beim Kopieren einer großen Anzahl von Dateien


12

Ich benutze den folgenden Befehl:

\cp -uf /home/ftpuser1/public_html/ftparea/*.jpg /home/ftpuser2/public_html/ftparea/

Und ich bekomme den Fehler:

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

Ich habe auch versucht:

ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} /home/ftpuser2/public_html/ftparea/

Ich habe immer noch -bash: / bin / ls: Argumentliste zu lang

Irgendwelche Ideen?


Ich versuche, alle JPGs von einem Verzeichnis in ein anderes zu kopieren, aber nur neue und aktualisierte Dateien.
icelizard

lsist nicht dafür ausgelegt. Verwenden Sie find.
Bis auf weiteres angehalten.

Das Problem liegt nicht bei ls, sondern bei der Anzahl der Argumente, die die Shell an ls übergibt. Sie würden den gleichen Fehler mit vi oder mit einem nicht eingebauten Befehl erhalten.
Chris

Ist aber lsvor allem nicht dafür ausgelegt: mywiki.wooledge.org/ParsingLs
Bis auf weiteres angehalten.

Stimmt, aber in diesem Fall liegt der Fehler nicht an einem Analysefehler mit ls, sondern an der Übergabe einer Milliarde Argumente an einen neuen Prozess, der zufällig ls ist. Es ist nicht nur eine unangemessene Verwendung von ls, sondern stößt auch auf eine Ressourcen- / Designbeschränkung von Unix. In diesem Fall hat der Patient sowohl Bauchschmerzen als auch ein gebrochenes Bein.
chris

Antworten:


18

* .jpg wird zu einer Liste erweitert, die länger ist, als die Shell verarbeiten kann. Versuchen Sie dies stattdessen

find  /home/ftpuser/public_html/ftparea/ -name "*.jpg" -exec cp -uf "{}" /your/destination \;

Ich habe find / home / ftpuser1 / public_html / ftparea / -name "* jpg" -exec cp -uf "{}" / home / ftpuser2 / public_html / ftparea / verwendet und den folgenden Fehler gefunden: fehlendes Argument für "-exec"
icelizard

Ihnen fehlt das letzte Argument von cp, sagte der Antwortende richtig. Überprüfen Sie Ihre Implementierung. Beachten Sie, dass in dieser Antwort der Punkt in "* .jpg" fehlt. Dies kann zu Fehlverhalten führen (z. B. ein Verzeichnis mit dem Namen "myjpg"). Beachten Sie dann, dass dies paranoisch sein kann, aber sicherer ist, genau anzugeben, was Sie mit der Datei -type kopieren möchten (um zu verhindern, dass Verzeichnisse, Symlinks usw. betroffen sind)
drAlberT

Bei näherer Betrachtung habe ich das "\;" um den Befehl zu beenden, den -exec ausführen soll. Wie dumm von mir!
icelizard

@AlberT: Danke für die Köpfe bezüglich des fehlenden Punktes. Das war ein Tippfehler. Antwort aktualisiert.
Shawn Chin

Es ist nicht so, dass cp damit nicht umgehen kann. Die Shell kann nicht.
d -_- b

6

Es gibt eine maximale Begrenzung für die Länge einer Argumentliste für Systembefehle. Diese Begrenzung ist distro-spezifisch und basiert auf dem Wert, zu MAX_ARG_PAGESdem der Kernel kompiliert wurde. Sie kann nicht geändert werden, ohne den Kernel neu zu kompilieren.

Aufgrund der Art und Weise, wie Globbing von der Shell verarbeitet wird, wirkt sich dies auf die meisten Systembefehle aus, wenn Sie dasselbe Argument ("* .jpg") verwenden. Da der Glob zuerst von der Shell verarbeitet und dann an den Befehl gesendet wird, lautet der Befehl:

cp -uf *.jpg /targetdir/

ist im Wesentlichen das gleiche für die Shell, als ob Sie geschrieben haben:

cp -uf 1.jpg 2.jpg ... n-1.jpg n.jpg /targetdir/

Wenn Sie mit vielen JPEGs zu tun haben, kann dies sehr schnell unüberschaubar werden. Abhängig von Ihrer Namenskonvention und der Anzahl der Dateien, die Sie tatsächlich verarbeiten müssen, können Sie den Befehl cp jeweils für eine andere Teilmenge des Verzeichnisses ausführen :

cp -uf /sourcedir/[a-m]*.jpg /targetdir/
cp -uf /sourcedir/[n-z]*.jpg /targetdir/

Dies könnte funktionieren, aber wie effektiv es wäre, hängt davon ab, wie gut Sie Ihre Dateiliste in praktische globbable Blöcke aufteilen können.

Globbable. Ich mag dieses Wort.

Einige Befehle, wie find und xargs , können große Dateilisten verarbeiten, ohne schmerzhaft große Argumentlisten zu erstellen.

find /sourcedir/ -name '*.jpg' -exec cp -uf {} /targetdir/ \;

Das Argument -exec führt den Rest der Befehlszeile einmal für jede von find gefundene Datei aus und ersetzt das {} durch jeden gefundenen Dateinamen. Da der Befehl cp jeweils nur für eine Datei ausgeführt wird, ist das Argumentlistenlimit kein Problem.

Dies kann langsam sein, da jede Datei einzeln verarbeitet werden muss. Die Verwendung von xargs könnte eine effizientere Lösung bieten:

find /sourcedir/ -name '*.jpg' -print0 | xargs -0 cp -uf -t /destdir/

xargs kann die vollständige Dateiliste von find in Argumentlisten mit überschaubaren Größen aufteilen und cp für jede dieser Unterlisten ausführen .

Natürlich gibt es auch die Möglichkeit, den Kernel einfach neu zu kompilieren und einen größeren Wert für festzulegen MAX_ARG_PAGES. Das Neukompilieren eines Kernels ist jedoch mehr Arbeit, als ich in dieser Antwort erklären möchte.


Ich habe keine Ahnung, warum dies abgelehnt wurde. Es ist die einzige Antwort, die zu erklären scheint, warum dies geschieht. Vielleicht, weil Sie nicht vorgeschlagen haben, xargs als Optimierung zu verwenden?
Chris

in der xargs-Lösung hinzugefügt, aber ich bin immer noch besorgt, dass die Abstimmungen auf etwas zurückzuführen sind, das in meinen Details offensichtlich nicht stimmt, und niemand möchte mir sagen, was es ist. :(
goldPseudo

xargsscheint viel effizienter zu sein, da die resultierende Anzahl von Befehlsaufrufen viel kleiner ist. In meinem Fall sehe ich eine 6-12-mal bessere Leistung bei der Verwendung argsals bei Verwendung einer -execLösung mit wachsender Anzahl von Dateien, da die Effizienz steigt.
Jan Vlcinsky

3

Dies liegt daran, dass Ihr Platzhalterausdruck ( *.jpg) beim Erweitern das Längenlimit für Befehlszeilenargumente überschreitet (wahrscheinlich, weil Sie viele JPG-Dateien unter haben /home/ftpuser/public_html/ftparea).

Es gibt verschiedene Möglichkeiten, diese Einschränkung zu umgehen, z. B. findoder xargs. In diesem Artikel finden Sie weitere Informationen dazu.


+1 für die gute externe Ressource zum Thema.
viam0Zah


2

Wie GoldPseudo kommentierte, gibt es eine Begrenzung für die Anzahl der Argumente, die Sie an einen Prozess übergeben können, den Sie erzeugen. In seiner Antwort finden Sie eine gute Beschreibung dieses Parameters.

Sie können das Problem vermeiden, indem Sie entweder nicht zu viele Argumente übergeben oder die Anzahl der übergebenen Argumente verringern.

Eine for-Schleife in der Shell, find und ls, grep und eine while-Schleife machen in dieser Situation alle dasselbe -

for file in /path/to/directory/*.jpg ; 
do
  rm "$file"
done

und

find /path/to/directory/ -name '*.jpg' -exec rm  {} \;

und

ls /path/to/directory/ | 
  grep "\.jpg$" | 
  while
    read file
  do
    rm "$file"
  done

Alle haben ein Programm, das das Verzeichnis liest (die Shell selbst, find und ls), und ein anderes Programm, das tatsächlich ein Argument pro Ausführung akzeptiert und die gesamte Befehlsliste durchläuft.

Dies ist nun langsam, da der RM für jede Datei, die dem * .jpg-Muster entspricht, gegabelt und ausgeführt werden muss.

Hier kommt xargs ins Spiel. xargs verwendet Standardeingaben und erzeugt für jede N-Zeile (für freebsd sind es standardmäßig 5000) ein Programm mit N Argumenten. xargs ist eine Optimierung der obigen Schleifen, da Sie nur 1 / N-Programme teilen müssen, um über den gesamten Satz von Dateien zu iterieren, die Argumente von der Befehlszeile lesen.


1

Der '*' Glob wird auf zu viele Dateinamen erweitert. Verwenden Sie stattdessen find / home / ftpuser / public_html -name '* .jpg'.


Suchen und Echo * führen zu derselben Ausgabe - der Schlüssel hier ist die Verwendung von xargs, wobei nicht nur alle 1 Milliarde Befehlszeilenargumente an den Befehl übergeben werden, den die Shell zu teilen versucht.
Chris

echo * schlägt fehl, wenn zu viele Dateien vorhanden sind, die Suche ist jedoch erfolgreich. Die Verwendung von find -exec mit + entspricht der Verwendung von xargs. (Nicht alle finden Unterstützung +)
William Pursell

1

Wenn Sie die +Option verwenden, find -execwird der Vorgang erheblich beschleunigt.

find  /home/ftpuser/public_html/ftparea/ -name "*jpg" -exec cp -uf -t /your/destination "{}" +

Die +Option muss {}das letzte Argument sein, damit die Funktion -t /your/destination(oder --target-directory=/your/destination) cpfunktioniert.

Von man find:

-exec Befehl {} +

          This  variant  of the -exec action runs the specified command on  
          the selected files, but the command line is built  by  appending  
          each  selected file name at the end; the total number of invoca  
          tions of the command will  be  much  less  than  the  number  of  
          matched  files.   The command line is built in much the same way  
          that xargs builds its command lines.  Only one instance of  ‘{}’  
          is  allowed  within the command.  The command is executed in the  
          starting directory.

Bearbeiten : Argumente in cp neu angeordnet


Ich erhalte Folgendes: fehlendes Argument für `-exec '/ home / ftpuser1 / public_html / ftparea / -name' * jpg '-exec cp -uf" {} "/ home / ftpuser2 / public_html / ftparea / +
icelizard

Ich habe die Argumente neu angeordnet cp, um diesen Fehler zu beheben.
Bis auf weiteres angehalten.

1

Es hört sich so an, als hätten Sie zu viele *.jpgDateien in diesem Verzeichnis, um sie alle gleichzeitig in die Befehlszeile zu stellen. Du könntest es versuchen:

find /home/ftpuser/public_html/ftparea1 -name '*.jpg' | xargs -I {} cp -uf {} /home/ftpuser/public_html/ftparea2/

Möglicherweise müssen Sie man xargsIhre Implementierung überprüfen , um festzustellen, ob der -ISwitch für Ihr System korrekt ist.

Wollen Sie diese Dateien wirklich an denselben Speicherort kopieren, an dem sie sich bereits befinden?


Entschuldigung, dies sind zwei verschiedene Verzeichnisse sollten ftpuser1 und ftpuser2 sein
icelizard

Ich habe es gerade versucht: ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} / home / ftpuser2 / public_html / ftparea / Immer noch -bash: / bin / ls: Argumentliste zu lang
icelizard

Oh, du hast ganz recht, natürlich lswird das gleiche Problem haben! Ich habe zu dem gewechselt, findwas nicht wird.
Greg Hewgill

0

Gehe zum Ordner

cd /home/ftpuser1/public_html/

und führen Sie Folgendes aus:

cp -R ftparea/ /home/ftpuser2/public_html/

Auf diese Weise kann sich der Ordner 'ftparea' negativ auswirken, wenn Sie nur die '* .jpg'-Dateien daraus haben möchten. Wenn jedoch keine Unterordner vorhanden sind, ist dieser Ansatz definitiv viel schneller als mit find und xargs

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.