Elenco argomenti troppo lungo durante la copia dei file

Ho appena fatto una domanda relativa a come posso contare i file di un’estensione particolare. Ora voglio cp questi file in una nuova dir .

Sto provando,

 cp *.prj ../prjshp/ 

e

 cp * | grep '\.prj$' ../prjshp/ 

ma stanno dando lo stesso errore,

bash: / bin / cp: lista degli argomenti troppo lunga

Come li copio?

cp *.prj ../prjshp/ è il comando giusto, ma hai cp *.prj ../prjshp/ un raro caso in cui viene eseguito un limite di dimensioni. Il secondo comando che hai provato non ha alcun senso.

Un metodo è quello di eseguire cp sui file in blocchi. Il comando find sa come fare:

 find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} + 
  • find attraversa la directory corrente e le directory sottostanti in modo ricorsivo.
  • -maxdepth 1 significa fermarsi a una profondità di 1, cioè non ricorrere in sottodirectory.
  • -name '*.prj' significa agire solo sui file il cui nome corrisponde al modello specificato. Nota le virgolette attorno al modello: sarà interpretato dal comando find , non dalla shell.
  • -exec … {} + significa eseguire il comando specificato per tutti i file. Invoca il comando più volte se necessario, facendo attenzione a non superare il limite della riga di comando.
  • mv -t ../prjshp sposta i file specificati in ../prjshp . L’opzione -t è usata qui a causa di una limitazione del comando find : i file trovati (simbolizzati da {} ) vengono passati come ultimo argomento del comando, non è ansible aggiungere la destinazione dopo di essa.

Un altro metodo è usare rsync .

 rsync -r --include='*.prj' --exclude='*' . ../prjshp 
  • rsync -r … . ../prjshp rsync -r … . ../prjshp copia la directory corrente in ../prjshp in ../prjshp ricorsivo.
  • --include='*.prj' --exclude='*' significa copiare i file corrispondenti a *.prj ed escludere tutto il resto (comprese le sottodirectory, quindi i file .prj nelle sottodirectory non saranno trovati).

Questo comando copia i file uno per uno e funzionerà anche se ce ne sono troppi per * per espandersi in un singolo comando cp :

 for i in *; do cp "$i" ../prjshp/; done 

IMHO, gli strumenti ottimali per gestire orde di file sono find e xargs . Vedi l’ man find . Vedi man xargs . find , con il suo switch -print0 , produce una lista NUL -separata di nomi di file (i nomi dei file possono contenere qualsiasi carattere exec NUL o / ) che xargs capisca, usando l’opzione -0 . xargs quindi costruisce il comando più lungo consentito (la maggior parte dei nomi di file, senza metà-nome file alla fine) e lo esegue. xargs ripete questo fino a quando find non fornisce più nomi di file. Esegui xargs --show-limits per vedere i limiti.

Per risolvere il tuo problema, (e dopo aver controllato man cp per trovare --target-directory= ):

 find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/ 

Ci sono 3 punti chiave da tenere a mente quando si affronta l’errore di Argument list too long :

  • La lunghezza degli argomenti della riga di comando è limitata dalla variabile ARG_MAX , che per definizione POSIX è “… [m] una lunghezza massima dell’argomento delle funzioni exec inclusi i dati dell’ambiente” (enfasi aggiunta) “. Cioè, quando shell esegue un comando non-built-it, deve chiamare uno di exec() per generare il processo di quel comando, ed è qui che entra in gioco ARG_MAX , il nome o il percorso del comando stesso (per esempio, /bin/echo ) suona un ruolo.

  • I comandi integrati di shell sono eseguiti dalla shell, il che significa che la shell non usa la famiglia di funzioni exec() e quindi non è influenzata dalla variabile ARG_MAX .

  • Alcuni comandi, come xargs e find sono a conoscenza della variabile ARG_MAX e ripetutamente eseguono azioni sotto quel limite

Dai punti precedenti e come mostrato nell’eccellente risposta di Kusalananda sulla domanda correlata, l’ Argument list too long degli Argument list too long può verificarsi anche quando l’ambiente è grande. Quindi, tenendo presente che l’ambiente di ciascun utente può variare, e la dimensione dell’argomento in byte è rilevante, è difficile trovare un singolo numero di file / argomenti.

Come gestire tale errore?

La cosa fondamentale è concentrarsi non sul numero di file, ma concentrarsi sull’opportunità o meno che il comando che si intende utilizzare coinvolga la famiglia di funzioni exec() e tangenzialmente – lo spazio dello stack.

Usa i built-in della shell

Come discusso in precedenza, i built-in della shell sono immuni al limite ARG_MAX , ovvero cose come loop, while loop, echo incorporato e built-in printf – tutti quelli che funzionano abbastanza bene.

 for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done 

Sulla domanda relativa all’eliminazione dei file, c’era una soluzione in quanto tale:

 printf '%s\0' *.jpg | xargs -0 rm -- 

Si noti che questo usa printf integrato nella shell. Se chiamiamo printf esterno, ciò implicherà exec() , quindi fallirà con un numero elevato di argomenti:

 $ /usr/bin/printf "%s\0" {1..7000000}> /dev/null bash: /usr/bin/printf: Argument list too long 

array di bash

Secondo una risposta di jlliagre, bash non impone limiti agli array, quindi si può anche build un array di nomi di file e usare slice per iterazione di loop, come mostrato nella risposta di danjpreron:

 files=( /path/to/old_dir/*.prj ) for((I=0;I<${#files[*]};I+=1000)); do cp -t /path/to/new_dir/ "${files[@]:I:1000}" done 

Questo, tuttavia, ha la limitazione di essere bash-specifici e non-POSIX.

Aumentare lo spazio dello stack

A volte puoi vedere persone suggerire di aumentare lo spazio di stack con ulimit -s ; su Linux il valore ARG_MAX è 1/4 dello spazio di stack per ogni programma, il che significa che l'aumento dello spazio di stack aumenta proporzionalmente lo spazio per gli argomenti.

 # getconf reports value in bytes, ulimit -s in kilobytes $ getconf ARG_MAX 2097152 $ echo $(( $(getconf ARG_MAX)*4 )) 8388608 $ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none 8388608 # Increasing stack space results in increated ARG_MAX value $ ulimit -s 16384 $ getconf ARG_MAX 4194304 

Secondo la risposta di Franck Dernoncourt , che cita Linux Journal, si può ricompilare anche il kernel Linux con un valore maggiore per le pagine di memoria massime per gli argomenti, tuttavia è più lavoro del necessario e apre il potenziale per gli exploit come affermato nell'articolo di Linux Journal citato.

Evita il guscio

Un altro modo è usare python o python3 che vengono di default con Ubuntu. L'esempio python + here-doc di seguito, è qualcosa che personalmente ho usato per copiare una grande directory di file da qualche parte nel range di 40.000 elementi:

 $ python < import shutil > import os > for f in os.listdir('.'): > if os.path.isfile(f): > shutil.copy(f,'./newdir/') > EOF 

Per gli attraversamenti ricorsivi, puoi usare os.walk .

Guarda anche:

  • Cosa definisce la dimensione massima per un comando singolo argomento?