¿Cómo resolver "mv: list de arguments demasiado larga"?

Tengo una carpeta con más de un millón de files que necesita sorting, pero realmente no puedo hacer nada porque mv emite este post todo el time

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

Estoy usando este command para mover files sin extensión:

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

xargs es la herramienta para el trabajo. Eso, o find con -exec … {} + . Estas herramientas ejecutan un command varias veces, con todos los arguments que se pueden pasar de una vez.

Ambos methods son más fáciles de llevar a cabo cuando la list de arguments de variables está al final, que no es el caso aquí: el argumento final para mv es el destino. Con las utilidades de GNU (es decir, en Linux o Cygwin no integrados), la opción -t a mv es útil para pasar el destino primero.

Si los nombres de file no tienen espacios en blanco ni ninguno de \"' , simplemente puede proporcionar los nombres de file como input a xargs (el command echo es un bash incorporado, por lo que no está sujeto al límite de longitud de línea de command):

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

Puede usar la opción -0 para xargs para usar input delimitada nula en lugar del formatting pnetworkingeterminado entre comillas.

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

Alternativamente, puede generar la list de nombres de file con find . Para evitar recurrencias en subdirectorys, use -type d -prune . Como no se especifica ninguna acción para los files de image listdos, solo se mueven los otros files.

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

(Esto incluye files de puntos, a diferencia de los methods comodín de shell).

Si no tiene utilidades GNU, puede usar un intérprete intermedio para get los arguments en el order 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, puede cargar el mv incorporado :

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

o si prefiere dejar que mv y otros nombres sigan consultando los commands externos:

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

o con globs estilo ksh:

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

Alternativamente, usando GNU mv y zargs :

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

El límite de aprobación de arguments del sistema operativo no se aplica a las expansiones que ocurren dentro del intérprete de shell. Entonces, además de usar xargs o find , podemos simplemente usar un loop de shell para dividir el procesamiento en commands mv individuales:

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

Esto usa solo características y utilidades del lenguaje de commands de shell de POSIX. Este delineador es más claro con indentación, con puntos y comas innecesarios eliminados:

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

Para get una solución más agresiva que las ofrecidas anteriormente, extraiga la fuente de su kernel y edite include/linux/binfmts.h

Aumente el tamaño de MAX_ARG_PAGES a algo mayor que 32. Esto aumenta la cantidad de memory que el núcleo permitirá para los arguments del progtwig, lo que le permite especificar su command mv o rm para un millón de files o lo que sea que esté haciendo. Recomstackr, instalar, reiniciar.

¡TENER CUIDADO! Si configura esto demasiado grande para la memory de su sistema, y ​​luego ejecuta un command con muchos arguments, ¡MALA COSA SUCEDERÁ! Sea extremadamente cauteloso al hacer esto en sistemas multiusuario, ¡hace que sea más fácil para los usuarios maliciosos utilizar toda su memory!

Si no sabes cómo recomstackr y reinstalar tu kernel manualmente, probablemente sea mejor que pretendas que esta respuesta no existe por el momento.

Una solución más simple que usa "$origin"/!(*.jpg|*.png|*.bmp) lugar de un bloque catch.

 for file in "$origin"/!(*.jpg|*.png|*.bmp); do mv -- "$file" "$destination" ; done 

Gracias a @Score_Under

Para un script de varias líneas, puede hacer lo siguiente (observe el ; antes de que se elimine el done ):

 for file in "$origin"/!(*.jpg|*.png|*.bmp); do # don't copy types *.jpg|*.png|*.bmp mv -- "$file" "$destination" done 

Para hacer una solución más generalizada que mueva todos los files, puede hacer una sola línea:

 for file in "$origin"/*; do mv -- "$file" "$destination" ; done 

Que se ve así si haces una sangría:

 for file in "$origin"/*; do mv -- "$file" "$destination" done 

Esto toma todos los files en el origen y los mueve uno por uno al destino. Las cotizaciones alnetworkingedor de $file son necesarias en caso de que haya espacios u otros caracteres especiales en los nombres de file.

Aquí hay un ejemplo de este método que funcionó perfectamente

 for file in "/Users/william/Pictures/export_folder_111210/"*.jpg; do mv -- "$file" "/Users/william/Desktop/southland/landingphotos/"; done 

Puede evitar esa restricción mientras sigue usando mv si no le importa ejecutarlo un par de veces.

Puede mover porciones a la vez. Digamos, por ejemplo, que tienes una larga list de nombres de files alfanuméricos.

 mv ./subdir/a* ./ 

Eso funciona. Luego noquee otro gran pedazo. Después de un par de movimientos, puede volver a usar mv ./subdir/* ./

Si trabajar con Linux kernel es suficiente, simplemente puede hacer

 ulimit -s 100000 

eso funcionará porque el kernel de Linux incluyó un parche hace unos 10 años que modificó el límite de arguments para basarse en el tamaño de la stack: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ commit /? id = b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

Actualización: si te sientes valiente, puedes decir

 ulimit -s unlimited 

y estarás bien con las expansiones de shell siempre que tengas suficiente RAM.