linux - too - Error de la lista de argumentos demasiado larga para los comandos rm, cp, mv
solve argument list too long (30)
tl; dr
Es una limitación del núcleo en el tamaño del argumento de la línea de comandos. Utilice un bucle for
lugar.
Origen del problema
Este es un problema del sistema, relacionado con execve
y la constante ARG_MAX
. Hay mucha documentación sobre eso (vea man execve , wiki de debian ).
Básicamente, la expansión produce un comando (con sus parámetros) que excede el límite ARG_MAX
. En el kernel 2.6.23
, el límite se estableció en 128 kB
. Esta constante se ha incrementado y puede obtener su valor ejecutando:
getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic
Solución: Usando for
Loop
Use un bucle for
como se recomienda en http://mywiki.wooledge.org/BashFAQ/095 y no hay límite, excepto el espacio de memoria / RAM:
for f in *.pdf; do rm "$f"; done
También es un enfoque portátil, ya que los glob tienen un comportamiento sólido y constante entre shells ( parte de la especificación POSIX ).
Nota: Como lo señalan varios comentarios, esto es de hecho más lento pero más fácil de mantener, ya que puede adaptar escenarios más complejos, por ejemplo , donde uno quiere hacer más que solo una acción.
Solución: usando find
Si insiste, puede usar find
pero realmente no usa xargs ya que "es peligroso (está roto, es explotable, etc.) cuando se lee una entrada no delimitada por NUL" :
find . -maxdepth 1 -name ''*.pdf'' -delete
El uso de -maxdepth 1 ... -delete
lugar de -exec rm {} +
permite find
simplemente las llamadas requeridas del sistema sin usar un proceso externo, por lo tanto más rápido (gracias al comentario de @chepner ).
Referencias
- http://mywiki.wooledge.org/BashFAQ/095 http://mywiki.wooledge.org/BashFAQ/095 @ wooledge
- execve (2): página de manual de Linux (búsqueda de ARG_MAX);
- Error: la lista de argumentos es demasiado larga en la wiki de Debian;
- ¿Por qué obtengo "/ bin / sh: lista de argumentos demasiado larga" al pasar los argumentos entre comillas? @ SuperUser
Tengo varios cientos de archivos PDF bajo un directorio en UNIX. Los nombres de los PDF son realmente largos (aprox. 60 caracteres).
Cuando trato de eliminar todos los PDF juntos usando el siguiente comando:
rm -f *.pdf
Obtuve el siguiente error:
/bin/rm: cannot execute [Argument list too long]
¿Cuál es la solución a este error? ¿Ocurre también este error para los comandos mv
y cp
? Si es así, ¿cómo resolver estos comandos?
Descubrí que para listas extremadamente grandes de archivos (> 1e6), estas respuestas eran demasiado lentas. Aquí hay una solución que usa procesamiento paralelo en Python. Lo sé, lo sé, esto no es Linux ... pero nada más aquí funcionó.
(Esto me ahorró horas)
# delete files
import os as os
import glob
import multiprocessing as mp
directory = r''your/directory''
os.chdir(directory)
files_names = [i for i in glob.glob(''*.{}''.format(''pdf''))]
# report errors from pool
def callback_error(result):
print(''error'', result)
# delete file using system command
def delete_files(file_name):
os.system(''rm -rf '' + file_name)
pool = mp.Pool(12)
# or use pool = mp.Pool(mp.cpu_count())
if __name__ == ''__main__'':
for file_name in files_names:
print(file_name)
pool.apply_async(delete_files,[file_name], error_callback=callback_error)
El comando rm tiene una limitación de archivos que puede eliminar simultáneamente.
Una posibilidad puede eliminarlos utilizando varias veces las bases de comandos rm en sus patrones de archivo, como:
rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf
También puede eliminarlos a través del comando de búsqueda :
find . -name "*.pdf" -exec rm {} /;
He enfrentado un problema similar cuando había millones de archivos de registro inútiles creados por una aplicación que llenaba todos los inodos. Recurrí a "localizar", coloqué todos los archivos "localizados" d en un archivo de texto y luego los eliminé uno por uno. Tomó un tiempo pero hizo el trabajo!
Intente esto también. Si desea eliminar archivos / carpetas de más de 30/90 días (+) o de menos de 30/90 (-) días, puede usar los siguientes comandos ex
Por ejemplo: para 90 días excluye lo anterior, después de que 90 días se eliminen los archivos / carpetas, esto significa 91,92 ... 100 días
find <path> -type f -mtime +90 -exec rm -rf {} /;
Por ejemplo: solo para los últimos archivos de 30 días que desea eliminar, use el siguiente comando (-)
find <path> -type f -mtime -30 -exec rm -rf {} /;
Si quieres giz los archivos por más de 2 días
find <path> -type f -mtime +2 -exec gzip {} /;
Si quieres ver los archivos / carpetas solo de un mes pasado. Ex:
find <path> -type f -mtime -30 -exec ls -lrt {} /;
Por encima de 30 días solo más, luego enumere los archivos / carpetas Ex:
find <path> -type f -mtime +30 -exec ls -lrt {} /;
find /opt/app/logs -type f -mtime +30 -exec ls -lrt {} /;
La razón por la que esto ocurre es porque bash realmente expande el asterisco a cada archivo coincidente, produciendo una línea de comandos muy larga.
Prueba esto:
find . -name "*.pdf" -print0 | xargs -0 rm
Advertencia: esta es una búsqueda recursiva y también encontrará (y eliminará) los archivos en los subdirectorios. Agregue -f
al comando rm solo si está seguro de que no quiere confirmación.
Puede hacer lo siguiente para hacer que el comando no recursivo:
find . -maxdepth 1 -name "*.pdf" -print0 | xargs -0 rm
Otra opción es usar la -delete
de find:
find . -name "*.pdf" -delete
La siguiente opción parece simple para este problema. Obtuve esta información de algún otro hilo pero me ayudó.
for file in /usr/op/data/Software/temp/application/openpages-storage/*; do
cp "$file" /opt/sw/op-storage/
done
Simplemente ejecute el comando anterior y hará la tarea.
Me encontré con este problema unas cuantas veces. Muchas de las soluciones ejecutarán el comando rm
para cada archivo individual que deba eliminarse. Esto es muy ineficiente:
find . -name "*.pdf" -print0 | xargs -0 rm -rf
Terminé escribiendo un script en python para eliminar los archivos basados en los primeros 4 caracteres en el nombre del archivo:
import os
filedir = ''/tmp/'' #The directory you wish to run rm on
filelist = (os.listdir(filedir)) #gets listing of all files in the specified dir
newlist = [] #Makes a blank list named newlist
for i in filelist:
if str((i)[:4]) not in newlist: #This makes sure that the elements are unique for newlist
newlist.append((i)[:4]) #This takes only the first 4 charcters of the folder/filename and appends it to newlist
for i in newlist:
if ''tmp'' in i: #If statment to look for tmp in the filename/dirname
print (''Running command rm -rf ''+str(filedir)+str(i)+''* : File Count: ''+str(len(os.listdir(filedir)))) #Prints the command to be run and a total file count
os.system(''rm -rf ''+str(filedir)+str(i)+''*'') #Actual shell command
print (''DONE'')
Esto funcionó muy bien para mí. Pude eliminar más de 2 millones de archivos temporales en una carpeta en aproximadamente 15 minutos. Comenté el contenido del código para que cualquier persona con un conocimiento mínimo o nulo de Python pueda manipular este código.
Me enfrentaba al mismo problema al copiar el directorio de origen del formulario al destino
directorio de origen tenía archivos ~ 3 lakcs
Utilicé cp con la opción -r y funcionó para mí
cp -r abc / def /
copiará todos los archivos desde abc a def sin avisar de la lista de argumentos demasiado larga
O puedes probar:
find . -name ''*.pdf'' -exec rm -f {} /;
Otra respuesta es forzar a xargs
a procesar los comandos en lotes. Por ejemplo, para delete
los archivos 100
a la vez, cd
en el directorio y ejecute esto:
echo *.pdf | xargs -n 100 rm
Para eliminar los primeros 100 archivos:
rm -rf ''ls | cabeza -100 ''
Para eliminar todos los *.pdf
en un directorio /path/to/dir_with_pdf_files/
mkdir empty_dir # Create temp empty dir
rsync -avh --delete --include ''*.pdf'' empty_dir/ /path/to/dir_with_pdf_files/
Eliminar archivos específicos a través de rsync
utilizando comodines es probablemente la solución más rápida en caso de que tenga millones de archivos. Y se hará cargo del error que está recibiendo.
(Paso opcional): DRY RUN. Para comprobar qué se eliminará sin eliminar. `
rsync -avhn --delete --include ''*.pdf'' empty_dir/ /path/to/dir_with_pdf_files/
. . .
Haz clic en los consejos y trucos de rsync para obtener más hacks rsync
Podrías usar una matriz bash:
files=(*.pdf)
for((I=0;I<${#files[*]};I+=1000)); do rm -f ${files[@]:I:1000}; done
De esta manera se borrarán en lotes de 1000 archivos por paso.
Puede crear una carpeta temporal, mover todos los archivos y subcarpetas que desea mantener en la carpeta temporal, luego eliminar la carpeta antigua y cambiar el nombre de la carpeta temporal a la carpeta anterior. Pruebe este ejemplo hasta que esté seguro de hacerlo en vivo:
mkdir testit
cd testit
mkdir big_folder tmp_folder
touch big_folder/file1.pdf
touch big_folder/file2.pdf
mv big_folder/file1,pdf tmp_folder/
rm -r big_folder
mv tmp_folder big_folder
rm -r big_folder
eliminará todos los archivos de big_folder
sin importar cuántos. Solo tiene que ser muy cuidadoso, primero tiene todos los archivos / carpetas que desea conservar, en este caso era file1.pdf
Sólo sé una manera de evitar esto. La idea es exportar esa lista de archivos pdf que tiene en un archivo. Luego divide ese archivo en varias partes. A continuación, elimine los archivos pdf que figuran en cada parte.
ls | grep .pdf > list.txt
wc -l list.txt
wc -l es contar cuántas líneas contiene list.txt. Cuando tenga la idea de cuánto tiempo es, puede decidir dividirlo por la mitad, hacia adelante o algo así. Usando el comando split -l Por ejemplo, divídalo en 600 líneas cada uno.
split -l 600 list.txt
esto creará unos cuantos archivos llamados xaa, xab, xac, etc., dependiendo de cómo se dividan. Ahora para "importar" cada lista en esos archivos en el comando rm, usa esto:
rm $(<xaa)
rm $(<xab)
rm $(<xac)
Perdón por mi mal ingles.
Si intenta eliminar una gran cantidad de archivos al mismo tiempo (eliminé un directorio con 485,000+ hoy), probablemente se encontrará con este error:
/bin/rm: Argument list too long.
El problema es que cuando escribe algo como rm -rf *
, el *
se reemplaza con una lista de todos los archivos coincidentes, como "rm -rf file1 file2 file3 file4" y así sucesivamente. Hay un búfer de memoria relativamente pequeño asignado para almacenar esta lista de argumentos y, si se llena, el shell no ejecutará el programa.
Para solucionar este problema, muchas personas usarán el comando buscar para encontrar cada archivo y pasarlos uno por uno al comando "rm" de esta manera:
find . -type f -exec rm -v {} /;
Mi problema es que necesitaba eliminar 500,000 archivos y tardaba demasiado.
Me topé con una forma mucho más rápida de eliminar archivos: ¡el comando "buscar" tiene una marca de "-eliminar" incorporada! Esto es lo que terminé usando:
find . -type f -delete
Con este método, estaba eliminando archivos a una velocidad de aproximadamente 2000 archivos / segundo, ¡mucho más rápido!
También puedes mostrar los nombres de archivo a medida que los elimines:
find . -type f -print -delete
... o incluso mostrar cuántos archivos se eliminarán, luego el tiempo que demora en eliminarlos:
root@devel# ls -1 | wc -l && time find . -type f -delete
100000
real 0m3.660s
user 0m0.036s
sys 0m0.552s
Si necesita mantener un servidor o sistema receptivo mientras elimina una gran cantidad de archivos, la sleep
entre cada instrucción de eliminación puede ser un buen método.
find . -name "*.pdf" -print0 | while read -d $''/0'' file
do
rm "$file"
sleep 0.005 # Sleeps for 5ms, tweak as needed
done
Si son nombres de archivo con espacios o caracteres especiales, use:
find -maxdepth 1 -name ''*.pdf'' -exec rm "{}" /;
Esta oración busca todos los archivos en el directorio actual (-maxdepth 1) con extensión pdf (-name ''* .pdf''), y luego, elimine cada uno (-exec rm "{}").
La expresión {} reemplaza el nombre del archivo y, "{}" establece el nombre del archivo como una cadena, incluidos espacios o caracteres especiales.
Si tiene problemas similares con grep , la solución más sencilla es retroceder un directorio y hacer una búsqueda recursiva.
Así que en lugar de
grep "something" *
puedes usar:
cd ..
grep "something" -R search_in_this_dir/
Tenga en cuenta que también buscará de forma recursiva las subcarpetas del directorio "search_in_this_dir_dir".
Supongamos que el nombre del directorio de entrada es entrada y el nombre del directorio de salida es la salida. A continuación, puede utilizar bucle simple para copiar todos
for f in input/*
do
cp $f output
done
Tuve el mismo problema con una carpeta llena de imágenes temporales que crecía día a día y este comando me ayudó a borrar la carpeta.
find . -name "*.png" -mtime +50 -exec rm {} /;
La diferencia con los otros comandos es el parámetro mtime que solo tomará los archivos más antiguos que X días (en el ejemplo 50 días)
Usando eso varias veces, disminuyendo en cada ejecución el rango de días, pude eliminar todos los archivos innecesarios
Una versión un poco más segura que usar xargs, también no recursiva: ls -p | grep -v ''/$'' | grep ''/.pdf$'' | while read file; do rm "$file"; done
ls -p | grep -v ''/$'' | grep ''/.pdf$'' | while read file; do rm "$file"; done
Filtrar nuestros directorios aquí es un poco innecesario ya que ''rm'' no lo eliminará de todos modos, y puede eliminarse por simplicidad, pero ¿por qué ejecutar algo que definitivamente devolverá un error?
Usar GNU en paralelo ( sudo apt install parallel
) es súper fácil
Ejecuta los comandos multiproceso donde ''{}'' es el argumento pasado
P.ej
ls /tmp/myfiles* | parallel ''rm {}''
Y otro:
cd /path/to/pdf
printf "%s/0" *.[Pp][Dd][Ff] | xargs -0 rm
printf
es un shell incorporado, y por lo que sé, siempre ha sido así. Ahora, dado que printf
no es un comando de shell (sino un componente), no está sujeto a "error de la argument list too long ...
".
Por lo tanto, podemos usarlo de forma segura con patrones de shell shell, como *.[Pp][Dd][Ff]
, luego canalizamos su salida para eliminar ( rm
) el comando, a través de xargs
, lo que garantiza que se ajuste a suficientes nombres de archivo en el comando línea para no fallar el comando rm
, que es un comando de shell.
El /0
en printf
sirve como separador nulo para los nombres de archivo que luego son procesados por el comando xargs
, usándolo ( -0
) como separador, por lo que rm
no falla cuando hay espacios en blanco u otros caracteres especiales en los nombres de archivo .
puedes probar esto
for f in *.pdf
do
rm $f
done
EDIT: El comentario de ThiefMaster me sugiere no divulgar una práctica tan peligrosa a los jóvenes jedis de shell, así que agregaré una versión más "segura" (para preservar las cosas cuando alguien tiene un archivo "-rf ... ..pdf")
echo "# Whooooo" > /tmp/dummy.sh
for f in ''*.pdf''
do
echo "rm -i $f" >> /tmp/dummy.sh
done
Después de ejecutar lo anterior, simplemente abra el archivo /tmp/dummy.sh en su favorito. edite y revise cada línea en busca de nombres de archivo peligrosos, comentándolos si se encuentran.
Luego copie el script dummy.sh en su directorio de trabajo y ejecútelo.
Todo esto por razones de seguridad.
puedes usar este elogio
find -name "*.pdf" -delete
find . -type f -name ''*xxx'' -print -delete
find
tiene una acción -delete
:
find . -maxdepth 1 -name ''*.pdf'' -delete