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