¿Resolver "mv: Lista de argumentos demasiado larga"?

¿Resolver "mv: Lista de argumentos demasiado larga"?

Tengo una carpeta con más de un millón de archivos que necesito ordenar, pero realmente no puedo hacer nada porque mvaparece este mensaje todo el tiempo.

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

Estoy usando este comando para mover archivos sin extensión:

mv -- !(*.jpg|*.png|*.bmp) targetdir/

Respuesta1

xargses la herramienta para el trabajo. Eso, ofindcon -exec … {} +. Estas herramientas ejecutan un comando varias veces, con tantos argumentos como se puedan pasar de una sola vez.

Ambos métodos son más fáciles de llevar a cabo cuando la lista de argumentos variables está al final, lo cual no es el caso aquí: el argumento final mves el destino. Con las utilidades GNU (es decir, en Linux o Cygwin no integrado), es útil la -topción mvde pasar el destino primero.

Si los nombres de los archivos no tienen espacios en blanco ni ninguno de \"'y no comienzan con -¹, entonces simplemente puede proporcionar los nombres de los archivos como entrada xargs(el echocomando es un bash incorporado, por lo que no está sujeto al límite de longitud de la línea de comando; Si ve !: event not found, debe habilitar la sintaxis global con shopt -s extglob):

echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir --

Puede utilizar la -0opción para xargsutilizar entradas delimitadas por nulos en lugar del formato entre comillas predeterminado.

printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir --

Alternativamente, puede generar la lista de nombres de archivos con find. Para evitar recurrir a subdirectorios, utilice -type d -prune. Dado que no se especifica ninguna acción para los archivos de imagen enumerados, solo se mueven los demás archivos.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec mv -t targetdir/ {} +

(Esto incluye archivos de puntos, a diferencia de los métodos comodín del shell).

Si no tiene utilidades GNU, puede usar un shell intermedio para obtener los argumentos en el orden correcto. Este método funciona en todos los sistemas POSIX.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec sh -c 'mv "$@" "$0"' targetdir/ {} +

En zsh, puedes cargar elmvincorporado:

setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/

o si prefieres dejar mvque otros nombres sigan haciendo referencia a los comandos externos:

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/

o con globos estilo ksh:

setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/

Alternativamente, usando GNU mvyzargs:

autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/ --

¹ con algunas xargsimplementaciones, los nombres de archivos también deben ser texto válido en la configuración regional actual. Algunos también considerarían _que un archivo nombrado indica el final de la entrada (se puede evitar con -E '')

Respuesta2

Si trabajar con el kernel de Linux es suficiente, simplemente puede hacer

ulimit -S -s unlimited

Eso funcionará porque el kernel de Linux incluyó un parche hace unos 10 años que cambió el límite de argumentos para que se basara en el tamaño de la pila:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

Si no desea espacio de pila ilimitado, puede decir, por ejemplo

ulimit -S -s 100000

para limitar la pila a 100 MB. Tenga en cuenta que debe configurar el espacio de la pila al uso normal de la pila (normalmente 8 MB) más el tamaño de la línea de comando que desea utilizar.

Puede consultar el límite real de la siguiente manera:

getconf ARG_MAX

eso generará la longitud máxima de la línea de comando en bytes. Por ejemplo, los valores predeterminados de Ubuntu establecen esto en 2097152aproximadamente 2 MB. Si ejecuto con una pila ilimitada, obtengo 4611686018427387903exactamente 2 ^ 62 o aproximadamente 46000 TB. Si su línea de comando excedeeso, Espero que pueda solucionar el problema usted mismo.

Tenga en cuenta que si usa sudoas en sudo mv *.dat somewhere/.ejecución ulimitno puede solucionar ese problema porque sudorestablece el tamaño de la pila antes de ejecutarlo mvde verdad. Para solucionar este problema, debe iniciar el shell raíz con sudo -s, luego ejecutar ulimit -S -s unlimitedy finalmente ejecutar el comando sin sudoese shell raíz.

Respuesta3

A veces es más fácil escribir un pequeño script, por ejemplo en Python:

import glob, shutil

for i in glob.glob('*.jpg'):
  shutil.move(i, 'new_dir/' + i)

Respuesta4

El límite de paso de argumentos del sistema operativo no se aplica a las expansiones que ocurren dentro del intérprete de shell. Entonces, además de usar xargso find, podemos simplemente usar un bucle de shell para dividir el procesamiento en mvcomandos individuales:

for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done

Esto utiliza únicamente funciones y utilidades del lenguaje de comandos POSIX Shell. Esta frase es más clara con sangría y se eliminan los puntos y coma innecesarios:

for x in *; do
  case "$x" in
    *.jpg|*.png|*.bmp) 
       ;; # nothing
    *) # catch-all case
       mv -- "$x" target
       ;;
  esac
done

información relacionada