Errore "Elenco argomenti troppo lungo" durante la copia di un numero elevato di file


12

Sto usando il seguente comando:

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

E sto ricevendo l'errore:

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

Ho anche provato:

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

Ancora ottenuto -bash: / bin / ls: elenco degli argomenti troppo lungo

Qualche idea?


Sto cercando di copiare tutti i jpg da 1 directory a un'altra, ma solo i nuovi file e quelli che sono stati aggiornati.
lucertola di ghiaccio

lsnon è progettato per fare questo tipo di cose. Usa find.
In pausa fino a ulteriore avviso.

Il problema non è con ls, è con il numero di argomenti che la shell sta passando a ls. Otterresti lo stesso errore con vi o con qualsiasi comando non incorporato.
chris,

Ma lsè soprattutto non progettato per fare questo: mywiki.wooledge.org/ParsingLs
in pausa fino a nuovo avviso.

È vero, ma in questo caso l'errore non è dovuto a un errore di analisi con ls, ma con il passaggio di un miliardo di argomenti a un nuovo processo che risulta essere ls. Oltre ad essere un uso inappropriato di ls, capita anche di imbattersi in una limitazione di risorse / progettazione di unix. In questo caso, il paziente ha sia un mal di stomaco che una gamba rotta.
chris,

Risposte:


19

* .jpg si espande in un elenco più lungo di quello che la shell può gestire. Prova questo invece

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

Ho usato find / home / ftpuser1 / public_html / ftparea / -name "* jpg" -exec cp -uf "{}" / home / ftpuser2 / public_html / ftparea / e ho trovato il seguente errore find: argomento mancante in `-exec '
lucertola di ghiaccio

Ti manca l'ultimo argomento di cp, il risponditore ti ha detto bene. Controlla la tua implementazione. Si noti che in questa risposta manca il punto in "* .jpg", ciò potrebbe comportare comportamenti errati (ad esempio cp una directory denominata "myjpg"). Si noti quindi che potrebbe essere paranoico ma più sicuro specificare da vicino ciò che si intende copiare utilizzando il file di tipo (evitando che directory, collegamenti simbolici e così via vengano influenzati)
drAlberT

Dopo un'attenta ispezione ho perso il "\;" per finire il comando che -exec dovrebbe eseguire. Sciocco me!
lucertola di ghiaccio

@AlberT: grazie per le teste riguardo al punto mancante. Quello era un errore di battitura. Risposta aggiornata
Shawn Chin,

Non è che cp non possa gestirlo. La shell non può.
d -_- b

6

Esiste un limite massimo alla durata di un elenco di argomenti per i comandi di sistema: questo limite è specifico della distro in base al valore di MAX_ARG_PAGESquando viene compilato il kernel e non può essere modificato senza ricompilare il kernel.

A causa del modo in cui la shell viene gestita dal globbing, ciò influirà sulla maggior parte dei comandi di sistema quando si utilizza lo stesso argomento ("* .jpg"). Poiché il glob viene prima elaborato dalla shell e quindi inviato al comando, il comando:

cp -uf *.jpg /targetdir/

è essenzialmente lo stesso per la shell come se avessi scritto:

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

Se hai a che fare con molti jpeg, questo può diventare ingestibile molto rapidamente. A seconda della convenzione di denominazione e del numero di file che è effettivamente necessario elaborare, è possibile eseguire il comando cp su un sottoinsieme diverso della directory alla volta:

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

Questo potrebbe funzionare, ma esattamente quanto sarebbe efficace si basa sulla capacità di suddividere l'elenco dei file in comodi blocchi globbable.

Globbable. Mi piace quella parola

Alcuni comandi, come find e xargs , possono gestire elenchi di file di grandi dimensioni senza creare elenchi di argomenti di dimensioni ridotte.

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

L'argomento -exec eseguirà il resto della riga di comando una volta per ogni file trovato da find , sostituendo {} con ogni nome file trovato. Poiché il comando cp viene eseguito solo su un file alla volta, il limite dell'elenco argomenti non è un problema.

Questo potrebbe essere lento a causa della necessità di elaborare ciascun file singolarmente. L'uso di xargs potrebbe fornire una soluzione più efficiente:

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

xargs può prendere l'elenco completo dei file fornito da find e suddividerlo in elenchi di argomenti di dimensioni gestibili ed eseguire cp su ciascuna di queste liste secondarie.

Naturalmente, c'è anche la possibilità di ricompilare il kernel, impostando un valore più grande per MAX_ARG_PAGES. Ma ricompilare un kernel è più lavoro di quello che sono disposto a spiegare in questa risposta.


Non ho idea del perché questo sia stato votato in negativo. È l'unica risposta che sembra spiegare perché questo sta accadendo. Forse perché non hai suggerito di usare xargs come ottimizzazione?
chris,

aggiunto nella soluzione xargs, ma sono ancora preoccupato che i downvotes siano dovuti a qualcosa di palesemente sbagliato nei miei dettagli e nessuno vuole dirmi di cosa si tratta. :(
goldPseudo

xargssembra essere molto più efficiente, poiché il numero risultante di chiamate di comando è molto più piccolo. Nel mio caso, vedo prestazioni 6-12 volte migliori quando si utilizza argsquindi quando si utilizza la -execsoluzione con un numero crescente di file è l'aumento dell'efficienza.
Jan Vlcinsky,

3

Ciò accade perché l'espressione jolly ( *.jpg) supera il limite di lunghezza dell'argomento della riga di comando quando viene espansa (probabilmente perché hai molti file .jpg sotto /home/ftpuser/public_html/ftparea).

Esistono diversi modi per aggirare quella limitazione, come usare findo xargs. Dai un'occhiata a questo articolo per maggiori dettagli su come farlo.


+1 per la buona risorsa esterna in materia.
viam0Zah,

3

Come ha commentato GoldPseudo, esiste un limite al numero di argomenti che è possibile passare a un processo che si sta generando. Vedi la sua risposta per una buona descrizione di quel parametro.

Puoi evitare il problema evitando di passare troppi argomenti al processo o riducendo il numero di argomenti che stai passando.

Un ciclo for nella shell, find, e ls, grep e un ciclo while fanno tutti la stessa cosa in questa situazione -

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

e

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

e

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

tutti hanno un programma che legge la directory (la shell stessa, trova e ls) e un programma diverso che prende effettivamente un argomento per esecuzione e scorre l'intero elenco di comandi.

Ora, questo sarà lento perché l'rm deve essere biforcato ed eseguito per ogni file che corrisponde al modello * .jpg.

È qui che entra in gioco xargs. xargs accetta un input standard e per ogni N (per default è 5000) righe, genera un programma con N argomenti. xargs è un'ottimizzazione dei cicli di cui sopra perché è necessario eseguire il fork dei programmi 1 / N per scorrere su tutta la serie di file che leggono gli argomenti dalla riga di comando.



1

Il glob '*' si sta espandendo a troppi nomi di file. Utilizzare invece / home / ftpuser / public_html -name '* .jpg'.


Find ed echo * producono lo stesso output - la chiave qui sta usando xargs non solo passando tutti gli 1 miliardo di argomenti della riga di comando al comando che la shell sta cercando di fork.
chris,

echo * fallirà se ci sono troppi file, ma find avrà esito positivo. Inoltre, usare find -exec con + equivale a usare xargs. (Non tutti trovano supporto +, però)
William Pursell,

1

L'uso +dell'opzione per find -execvelocizzerà notevolmente l'operazione.

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

L' +opzione richiede {}di essere l'ultimo argomento, quindi utilizzando l' opzione -t /your/destination(o --target-directory=/your/destination) per cpfarlo funzionare.

Da man find:

comando -exec {} +

          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.

Modifica : riordina gli argomenti in cp


Ricevo l'argomento: argomento mancante in `-exec '/ home / ftpuser1 / public_html / ftparea / -name' * jpg '-exec cp -uf" {} "/ home / ftpuser2 / public_html / ftparea / +
icelizard

Ho riorganizzato gli argomenti cpper correggere quell'errore.
In pausa fino a ulteriore avviso.

1

Sembra che tu abbia troppi *.jpgfile in quella directory per metterli tutti sulla riga di comando contemporaneamente. Puoi provare:

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

Potrebbe essere necessario verificare man xargsl'implementazione per vedere se lo -Iswitch è corretto per il proprio sistema.

In realtà, hai davvero intenzione di copiare quei file nella stessa posizione in cui si trovano già?


ci scusiamo che queste due diverse directory dovrebbero essere ftpuser1 e ftpuser2
icelizard,

Ho appena provato questo: ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} / home / ftpuser2 / public_html / ftparea / Ancora ottenuto -bash: / bin / ls: Elenco degli argomenti troppo lungo
icelizard

Oh, hai perfettamente ragione, ovviamente lsavrà lo stesso problema! Sono passato al findquale non lo farò.
Greg Hewgill,

0

Vai alla cartella

cd /home/ftpuser1/public_html/

ed eseguire quanto segue:

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

In questo modo se la cartella 'ftparea' ha sottocartelle, questo potrebbe essere un effetto negativo se si desidera solo i file '* .jpg' da esso, ma se non ci sono sottocartelle, questo approccio sarà sicuramente molto più veloce di usando find e xargs

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.