Elenco degli argomenti troppo lungo durante la copia dei file


26

Ho appena fatto una domanda relativa a come posso contare i file di una determinata estensione. Ora voglio cpquesti file per un nuovo dir.

Sto provando,

cp *.prj ../prjshp/

e

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

ma stanno dando lo stesso errore,

bash: / bin / cp: elenco degli argomenti troppo lungo

Come li copio?


Risposte:


36

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

Un metodo consiste cpnell'eseguire i file in blocchi. Il findcomando sa come fare:

find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
  • find attraversa ricorsivamente la directory corrente e le directory sottostanti.
  • -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 findcomando, non dalla shell.
  • -exec … {} +significa eseguire il comando specificato per tutti i file. Richiama il comando più volte se necessario, facendo attenzione a non superare il limite della riga di comando.
  • mv -t ../prjshpsposta i file specificati in ../prjshp. L' -topzione viene utilizzata qui a causa di una limitazione del findcomando: i file trovati (simboleggiati da {}) vengono passati come ultimo argomento del comando, non è possibile aggiungere la destinazione dopo di esso.

Un altro metodo è usare rsync.

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

3
rsync, di gran lunga la soluzione più semplice qui.
ntk4,

Per essere in qualche modo nitido, il secondo comando cp * | grep '\.prj$' ../prjshp/ non ha alcun senso, ma può essere sintatticamente valido, se si *espande all'elenco dei file con l'ultimo che è una directory (aka cp SOURCE1 SOURCE2....DEST). La pipe non ha alcun senso, certo, ma rimane anche sintatticamente valida per quanto riguarda la shell: dup()i descrittori dei file andranno bene, è solo che l'estremità del lettore della pipe non otterrà alcun dato perché cpnon scrive alcun .
Sergiy Kolodyazhnyy,

Sia find che rsync hanno prodotto per me lo stesso elenco di argomenti errore troppo lungo. Il ciclo for era la soluzione più semplice.
Meezaan-ud-Din,

In effetti rsync è il modo di fare qualsiasi copia di massa, anche se sono sconcertato da quanto siamo arrivati ​​con Linux e abbiamo un difetto / bug stupido come questo e sì lo considererei un difetto / bug.
MitchellK,

22

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

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

Questo funziona per me.
1rq3fea324wre

1
Semplice ed efficace Ho avuto un problema simile rimuovendo ~ 1/4 milioni di jpeg che avevo estratto da un video per un progetto. Questo è l'approccio che ho usato.
Elder Geek,

5

Ci sono 3 punti chiave da tenere a mente di fronte Argument list too longall'errore:

  • La lunghezza degli argomenti della riga di comando è limitata dalla ARG_MAXvariabile, che per definizione POSIX è "... [m] la lunghezza massima dell'argomento per le funzioni exec, inclusi i dati ambientali" (enfasi aggiunta) ". Cioè, quando la shell esegue un non -built-it command, deve chiamarne uno exec()per generare il processo di quel comando, ed è qui che ARG_MAXentra in gioco.Inoltre, il nome o il percorso del comando stesso (per esempio, /bin/echo) gioca un ruolo.

  • I comandi integrati della shell vengono eseguiti dalla shell, il che significa che la shell non utilizza la exec()famiglia di funzioni e quindi non è influenzata dalla ARG_MAXvariabile.

  • Alcuni comandi, come xargse findsono a conoscenza della ARG_MAXvariabile ed eseguono ripetutamente azioni al di sotto di tale limite

Dai punti sopra e come mostrato nell'eccellente risposta di Kusalananda sulla domanda correlata, ciò Argument list too longpuò accadere anche quando l'ambiente è grande. Quindi, considerando 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 sul fatto che il comando che si intende utilizzare coinvolga o meno la exec()famiglia di funzioni e, tangenzialmente, lo spazio dello stack.

Usa gli incorporamenti della shell

Come discusso in precedenza, i built-in della shell sono immuni al ARG_MAXlimite, vale a dire forloop, whileloop, built-in echoe built-in printf- tutti quelli funzioneranno abbastanza bene.

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

Sulla domanda correlata sull'eliminazione dei file, c'era una soluzione in quanto tale:

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

Si noti che questo utilizza la shell integrata printf. Se chiamiamo l'esterno printf, ciò comporterà exec(), quindi fallirà con un gran numero di argomenti:

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

bash array

Secondo una risposta di jlliagre, bashnon si impongono limiti alle matrici, quindi è possibile fare anche la creazione di array di nomi di file e l'utilizzo di sezioni 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

Ciò, tuttavia, ha il limite di essere specifico per bash e non POSIX.

Aumenta lo spazio dello stack

A volte puoi vedere le persone suggerire di aumentare lo spazio dello stack con ulimit -s <NUM>; su Linux il valore ARG_MAX è 1/4 dello spazio dello stack per ciascun programma, il che significa che aumentare lo spazio dello 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ò anche ricompilare il kernel Linux con un valore maggiore per le pagine di memoria massima per argomenti, tuttavia, è più lavoro del necessario e apre il potenziale per gli exploit, come indicato nell'articolo citato del Linux Journal.

Evita le coperture

Un altro modo, è quello di utilizzare pythono python3che vengono di default con Ubuntu. Il pitone + qui-doc esempio riportato di seguito, è qualcosa che ho personalmente usato per copiare una grande directory di file da qualche parte nella gamma di 40.000 articoli:

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

Per gli attraversamenti ricorsivi, è possibile utilizzare os.walk .

Guarda anche:


2

IMHO, gli strumenti ottimali per gestire orde di file sono finde xargs. Vedere man find. Vedere man xargs. find, con la sua opzione -print0, produce un NULelenco separato di nomi di file (i nomi di file possono contenere qualsiasi carattere eseguito NULo /) che xargscapisce, usando l' opzione -0. xargscrea quindi il comando più lungo consentito (il maggior numero di nomi di file, senza mezzo nome file alla fine) e lo esegue. xargslo ripete fino a quando findnon vengono più forniti nomi di file. Corri xargs --show-limits </dev/nullper vedere i limiti.

Per risolvere il tuo problema, (e dopo aver verificato man cpper trovare --target-directory=):

find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
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.