Ci sono 3 punti chiave da tenere a mente di fronte Argument list too long
all'errore:
La lunghezza degli argomenti della riga di comando è limitata dalla ARG_MAX
variabile, 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_MAX
entra 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_MAX
variabile.
Alcuni comandi, come xargs
e find
sono a conoscenza della ARG_MAX
variabile 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 long
può 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_MAX
limite, vale a dire for
loop, while
loop, built-in echo
e 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, bash
non 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 python
o python3
che 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: