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?

icelizard
quelle
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 \;
Shawn Chin
quelle
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.

goldPseudo
quelle
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.

mfriedman
quelle
+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.

chris
quelle
1

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

William Pursell
quelle
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

Bis auf weiteres angehalten.
quelle
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?

Greg Hewgill
quelle
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

Pinpinokio
quelle