Tengo una carpeta con más de un millón de archivos que necesita ser ordenada, pero realmente no puedo hacer nada porque mv
genera 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/
Respuestas:
xargs
Es la herramienta para el trabajo. Eso ofind
con-exec … {} +
. Estas herramientas ejecutan un comando varias veces, con tantos argumentos como se pueden pasar de una 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 no integrado o Cygwin), la-t
opciónmv
es útil, para pasar el destino primero.Si los nombres de archivo no tienen espacios en blanco ni ninguno
\"'
, simplemente puede proporcionar los nombres de archivo como entradaxargs
(elecho
comando es un bash incorporado, por lo que no está sujeto al límite de longitud de la línea de comando):Puede usar la
-0
opción paraxargs
usar una entrada delimitada por nulos en lugar del formato predeterminado entre comillas.Alternativamente, puede generar la lista de nombres de archivo con
find
. Para evitar recurrir a subdirectorios, use-type d -prune
. Como no se especifica ninguna acción para los archivos de imagen enumerados, solo se mueven los otros archivos.(Esto incluye archivos de puntos, a diferencia de los métodos comodín de 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.
En zsh, puede cargar el
mv
archivo incorporado :o si prefiere dejar que
mv
otros nombres sigan refiriéndose a los comandos externos:o con globos de estilo ksh:
Alternativamente, usando GNU
mv
yzargs
:fuente
shopt -s extglob
que habilitarlo. Me perdí un paso en losfind
comandos, los arreglé.find
comandos que he publicado (ahora) funcionan. Debe haber dejado una parte al copiar y pegar.!
? Es más explícito y más fácil de entender que el final extraño-o
. Por ejemplo,! -name '*.jpg' -a ! -name '*.png' -a ! -name '*.bmp'
Si trabajar con el kernel de Linux es suficiente, simplemente puede hacer
eso funcionará porque el kernel de Linux incluyó un parche hace unos 10 años que cambió el límite de argumento para basarse en el tamaño de la pila: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ commit /? id = b6a2fea39318e43fee84fa7b0b90d68bed92d2ba
Actualización: si te sientes valiente, puedes decir
y estará bien con cualquier expansión de shell siempre que tenga suficiente RAM.
fuente
ulimit -s unlimited
y funcionará para archivos prácticamente ilimitados.ulimit -s unlimited
el límite de línea de comando real es 2 ^ 31 o 2 GB. (MAX_ARG_STRLEN
en la fuente del núcleo.)El límite de aprobación 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
ofind
, simplemente podemos usar un bucle de shell para dividir el procesamiento enmv
comandos individuales :Esto usa solo las características y utilidades del lenguaje de comandos de shell POSIX. Esta línea es más clara con sangría, con puntos y comas innecesarios eliminados:
fuente
mv
procesos, en lugar de solo los pocos necesarios con lafind
solución POSIX que @Gilles publicó. En otras palabras, de esta manera se producen muchas pérdidas innecesarias de CPU.case
declaración sobre el resultado de la*
expansión para filtrar varias extensiones es equivalente a la!(*.jpg|*.png|*.bmp)
expresión original . Lafind
respuesta, de hecho, no es equivalente; desciende a subdirectorios (no veo un-maxdepth
predicado).-name . -o -type d -prune -o
protege de descender a subdirectorios.-maxdepth
aparentemente no es compatible con POSIX, aunque eso no se menciona en mifind
página de manual.Para obtener una solución más agresiva que las ofrecidas anteriormente, abra la fuente de su núcleo y edite
include/linux/binfmts.h
Aumente el tamaño
MAX_ARG_PAGES
a algo más grande que 32. Esto aumenta la cantidad de memoria que el núcleo permitirá para argumentos de programa, lo que le permite especificar sumv
orm
comando para un millón de archivos o lo que sea que esté haciendo. Recompilar, instalar, reiniciar.¡TENER CUIDADO! Si configura esto demasiado grande para la memoria de su sistema, y luego ejecuta un comando con muchos argumentos ¡MALAS COSAS OCURRIRÁN! Sea extremadamente cauteloso al hacer esto con los sistemas multiusuario, ¡facilita a los usuarios maliciosos el uso de toda su memoria!
Si no sabe cómo volver a compilar y reinstalar su núcleo manualmente, probablemente sea mejor que simplemente finja que esta respuesta no existe por ahora.
fuente
Una solución más simple usando en
"$origin"/!(*.jpg|*.png|*.bmp)
lugar de un bloque catch:Gracias a @Score_Under
Para una secuencia de comandos de varias líneas, puede hacer lo siguiente (observe
;
antes de quedone
se elimine):Para hacer una solución más generalizada que mueva todos los archivos, puede hacer una línea:
Que se ve así si haces sangría:
Esto toma cada archivo en el origen y los mueve uno por uno al destino. Las comillas
$file
son necesarias en caso de que haya espacios u otros caracteres especiales en los nombres de archivo.Aquí hay un ejemplo de este método que funcionó perfectamente
fuente
!(*.jpg|*.png|*.bmp)
. Puede agregar eso a su bucle for haciendo un globo"$origin"/!(*.jpg|*.png|*.bmp)
que evitaría la necesidad del interruptor utilizado en la respuesta de Kaz y mantener el cuerpo simple del bucle for.A veces es más fácil escribir un pequeño script, por ejemplo, en Python:
fuente
Puede sortear esa restricción mientras la sigue usando
mv
si no le importa ejecutarla un par de veces.Puede mover porciones a la vez. Digamos, por ejemplo, que tenía una larga lista de nombres de archivos alfanuméricos.
Eso funciona. Luego elimine otro gran pedazo. Después de un par de movimientos, puede volver a usar
mv ./subdir/* ./
fuente
Aquí están mis dos centavos, agregue esto a
.bash_profile
Uso
fuente