Tengo una carpeta con más de un millón de archivos que necesito ordenar, pero realmente no puedo hacer nada porque mv
aparece 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
xargs
es la herramienta para el trabajo. Eso, ofind
con -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 mv
es el destino. Con las utilidades GNU (es decir, en Linux o Cygwin no integrado), es útil la -t
opción mv
de 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 echo
comando 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 -0
opción para xargs
utilizar 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 elmv
incorporado:
setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/
o si prefieres dejar mv
que 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 mv
yzargs
:
autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/ --
¹ con algunas xargs
implementaciones, 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 2097152
aproximadamente 2 MB. Si ejecuto con una pila ilimitada, obtengo 4611686018427387903
exactamente 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 sudo
as en sudo mv *.dat somewhere/.
ejecución ulimit
no puede solucionar ese problema porque sudo
restablece el tamaño de la pila antes de ejecutarlo mv
de verdad. Para solucionar este problema, debe iniciar el shell raíz con sudo -s
, luego ejecutar ulimit -S -s unlimited
y finalmente ejecutar el comando sin sudo
ese 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 xargs
o find
, podemos simplemente usar un bucle de shell para dividir el procesamiento en mv
comandos 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