¿Cómo elimino todos los archivos menos 10 en Linux?
49
Estoy tratando de mantener un directorio lleno de archivos de registro manejables. Todas las noches, quiero eliminar todos menos los 10 más recientes. ¿Cómo puedo hacer esto en un solo comando?
@michael ¿Cómo puede ser el mío un duplicado de ese, si mi discusión se creó primero?
dev4life
@ovaherenow "tuyo"? No veo tu nombre en ninguna de las preguntas, así que no estoy seguro de a qué te refieres. Mi comentario ni siquiera indica cuál es un duplicado del otro. Pero no importa, es solo un comentario, con un enlace. Se podría agregar a cualquiera de las preguntas o a las dos preguntas. Depende de otra persona, un administrador, marcar uno u otro como duplicado. No se pretendía ningún juego nefasto ni se debería percibir ninguno. Más importante que qué pregunta fue primero, es qué pregunta tiene la mejor respuesta; nuevamente, el enlace facilita esta discusión, no determina la respuesta.
michael
@michael wow. Eso es realmente extraño. Abrí este hilo pero obviamente ese no es mi nombre de usuario. Pero recibo notificaciones sobre este hilo.
dev4life
Respuestas:
58
Para una solución portátil y confiable, intente esto:
ls -1tr | head -n -10 | xargs -d '\n' rm -f --
La tail -n -10sintaxis en una de las otras respuestas no parece funcionar en todas partes (es decir, no en mis sistemas RHEL5).
Y usar $()o ``en la línea de comando rmcorre el riesgo de
división de nombres de archivos con espacios en blanco, y
exceder el límite máximo de caracteres de la línea de comandos.
xargssoluciona ambos problemas porque automáticamente calculará cuántos args puede pasar dentro del límite de caracteres, y con el -d '\n'solo se dividirá en el límite de línea de la entrada. Técnicamente, esto todavía puede causar problemas para los nombres de archivo con una nueva línea en ellos, pero eso es mucho menos común que los nombres de archivo con espacios, y la única forma de evitar las nuevas líneas sería mucho más complicada, probablemente con al menos awk, si no es que perl.
Si no tiene xargs (¿antiguos sistemas AIX, tal vez?), Podría hacer un bucle:
ls -1tr | head -n -10 | while IFS= read -r f; do
rm -f "$f"
done
Esto será un poco más lento porque genera un rmarchivo separado para cada archivo, pero seguirá evitando las advertencias 1 y 2 anteriores (pero aún sufre de nuevas líneas en los nombres de archivo).
@HaukeLaging: Desde la head(1)página del manual:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Isaac Freeman
En Solaris 10 heady xargsdar errores. Las opciones correctas son head -n 10y xargs rm -f. El comando completo esls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov
1
Tenga en cuenta que ls -1tres el número UNO, no la letra "L".
Usuario de Linux shonky
1
También debe tenerse en cuenta que las líneas nuevas son caracteres raros pero válidos en los nombres de archivo ext. Esto significa que si tiene los archivos "that" y "that \ nthing", si este script toca "that \ nthing", eliminará "that", error cuando no pudo eliminar "thing", y se irá "that \ nthing" (el objetivo previsto) no se ve afectado.
Synthead
1
@IsaacFreeman He eliminado mis malos consejos, saludos.
Walf
20
El código que desea incluir en su secuencia de comandos es
rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)
La -1opción (número uno) imprime cada archivo en una sola línea, para estar seguro. La -fopción rmle dice que ignore los archivos inexistentes para cuando lsno devuelve nada.
Cartel original, aquí. Intenté este comando, pero no funciona: aparece el error "rm: missing
operand
2
¿Utilizaste la ruta correcta? Obviamente /path/to/your/logssolo está hecho para que usted ingrese la ruta correcta. Para encontrar el error, ejecute las partes ls -t /path/...y ls -t /path/... | tail -n +11solo y verifique si el resultado es correcto.
Daniel Böhmer
1
@HaukeLaging Estoy bastante seguro de que te equivocaste. Cuidado con el +antes del número. No tailimprime los últimos n elementos, sino todos los elementos que comienzan desde el n °. Me registré nuevamente y el comando definitivamente debería eliminar solo los elementos anteriores a los 10 archivos más recientes en todas las circunstancias.
Daniel Böhmer
@halo De hecho, lo siento.
Hauke Laging
2
Creo que su respuesta es muy peligrosa lsimprime un nombre de archivo, no una ruta, por rm -flo que intentará eliminar los archivos en el directorio actual
-mtime 10 -deletesolo elimina archivos modificados exactamente hace 10 días. Los archivos más antiguos no se eliminarán. -mtime +11 -deleteeliminará los archivos que se hayan modificado hace 11 o más días, dejando los últimos 10 días de archivos de registro.
Markus
1
Creo que el uso de en %plugar de %Pes necesario printfpara que los archivos tengan la ruta completa. De lo contrario rm -rf, no funciona.
jalopaba
9
Una herramienta como logrotate hace esto por usted. Hace que la administración de registros sea mucho más fácil. También puede incluir rutinas de limpieza adicionales, como sugirió Halo.
#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.
if [ $# -ne 2 ]; then
echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
exit 1
fi
cd $1
files_in_dir=`ls | wc -l`
files_to_delete=`expr $files_in_dir - $2`
if [ $files_to_delete -gt 0 ]; then
ls -t | tail -n $files_to_delete | xargs rm
if [ $? -ne 0 ]; then
echo "An error ocurred deleting the files"
exit 1
else
echo "$files_to_delete file(s) deleted."
fi
else
echo "nothing to delete!"
fi
Gracias por proporcionar esta respuesta. Si bien proporcionar un código como este es útil, también ayuda a proporcionar información adicional sobre cómo podría usarse o qué hace el código. Puede usar el botón editar para realizar mejoras futuras en su publicación.
nhinkle
1
En Bash puedes probar:
ls -1t | (i=0; while read f; do
if [ $i -lt 10 ]; then
((i++))
continue
else
rm -f "$f"
fi
done)
Esto omite los 10 als más nuevos y elimina el resto. logrotate puede ser mejor, solo quiero corregir las respuestas incorrectas relacionadas con el shell.
No estoy seguro de que esto ayude a nadie, pero para evitar posibles problemas con los caracteres inestables que acabo de usar lspara realizar la clasificación, pero luego uso los archivos inodepara la eliminación.
Para el directorio actual, esto mantiene los 10 archivos más recientes según la hora de modificación.
Necesitaba una solución elegante para el busybox (enrutador), todas las soluciones de xargs o array me resultaron inútiles; no hay tal comando disponible allí. find and mtime no es la respuesta adecuada, ya que estamos hablando de 10 elementos y no necesariamente de 10 días. La respuesta de Espo fue la más corta, más limpia y probablemente la más irreversible.
El error con espacios y cuando no se van a eliminar archivos simplemente se resuelve de la manera estándar:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
Versión un poco más educativa: podemos hacerlo todo si usamos awk de manera diferente. Normalmente, uso este método para pasar (devolver) variables de awk a sh. Como leemos todo el tiempo que no se puede hacer, ruego diferir: aquí está el método.
Ejemplo para archivos .tar sin problemas con respecto a los espacios en el nombre del archivo. Para probar, reemplace "rm" con el "ls".
En el caso de usar el ls -tcomando no hará ningún daño en ejemplos tan tontos como: touch 'foo " bar'y touch 'hello * world'. ¡No es que alguna vez creamos archivos con esos nombres en la vida real!
Nota al margen. Si quisiéramos pasar una variable al sh de esta manera, simplemente modificaríamos la impresión:
print "VarName="$1
para establecer la variable VarNameal valor de $1. Se pueden crear múltiples variables de una vez. Esto se VarNameconvierte en una variable sh normal y puede usarse normalmente en un script o shell después. Entonces, para crear variables con awk y devolverlas al shell:
Respuestas:
Para una solución portátil y confiable, intente esto:
La
tail -n -10
sintaxis en una de las otras respuestas no parece funcionar en todas partes (es decir, no en mis sistemas RHEL5).Y usar
$()
o``
en la línea de comandorm
corre el riesgo dexargs
soluciona ambos problemas porque automáticamente calculará cuántos args puede pasar dentro del límite de caracteres, y con el-d '\n'
solo se dividirá en el límite de línea de la entrada. Técnicamente, esto todavía puede causar problemas para los nombres de archivo con una nueva línea en ellos, pero eso es mucho menos común que los nombres de archivo con espacios, y la única forma de evitar las nuevas líneas sería mucho más complicada, probablemente con al menos awk, si no es que perl.Si no tiene xargs (¿antiguos sistemas AIX, tal vez?), Podría hacer un bucle:
Esto será un poco más lento porque genera un
rm
archivo separado para cada archivo, pero seguirá evitando las advertencias 1 y 2 anteriores (pero aún sufre de nuevas líneas en los nombres de archivo).fuente
head(1)
página del manual:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
head
yxargs
dar errores. Las opciones correctas sonhead -n 10
yxargs rm -f
. El comando completo esls -1tr ./ | head -n 10 | xargs rm -rf
ls -1tr
es el número UNO, no la letra "L".El código que desea incluir en su secuencia de comandos es
La
-1
opción (número uno) imprime cada archivo en una sola línea, para estar seguro. La-f
opciónrm
le dice que ignore los archivos inexistentes para cuandols
no devuelve nada.fuente
/path/to/your/logs
solo está hecho para que usted ingrese la ruta correcta. Para encontrar el error, ejecute las partesls -t /path/...
yls -t /path/... | tail -n +11
solo y verifique si el resultado es correcto.+
antes del número. Notail
imprime los últimos n elementos, sino todos los elementos que comienzan desde el n °. Me registré nuevamente y el comando definitivamente debería eliminar solo los elementos anteriores a los 10 archivos más recientes en todas las circunstancias.ls
imprime un nombre de archivo, no una ruta, porrm -f
lo que intentará eliminar los archivos en el directorio actualAparentemente analizar
ls
es malo .Si cada archivo se crea a diario y desea mantener los archivos creados en los últimos 10 días, puede hacer lo siguiente:
O si cada archivo se crea arbitrariamente:
fuente
-mtime 10 -delete
solo elimina archivos modificados exactamente hace 10 días. Los archivos más antiguos no se eliminarán.-mtime +11 -delete
eliminará los archivos que se hayan modificado hace 11 o más días, dejando los últimos 10 días de archivos de registro.%p
lugar de%P
es necesarioprintf
para que los archivos tengan la ruta completa. De lo contrariorm -rf
, no funciona.Una herramienta como logrotate hace esto por usted. Hace que la administración de registros sea mucho más fácil. También puede incluir rutinas de limpieza adicionales, como sugirió Halo.
fuente
He modificado el enfoque de Isaac un poco.
Ahora funciona con una ruta deseada:
fuente
Desde aquí :
fuente
En Bash puedes probar:
Esto omite los 10 als más nuevos y elimina el resto. logrotate puede ser mejor, solo quiero corregir las respuestas incorrectas relacionadas con el shell.
fuente
No estoy seguro de que esto ayude a nadie, pero para evitar posibles problemas con los caracteres inestables que acabo de usar
ls
para realizar la clasificación, pero luego uso los archivosinode
para la eliminación.Para el directorio actual, esto mantiene los 10 archivos más recientes según la hora de modificación.
ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;
fuente
Necesitaba una solución elegante para el busybox (enrutador), todas las soluciones de xargs o array me resultaron inútiles; no hay tal comando disponible allí. find and mtime no es la respuesta adecuada, ya que estamos hablando de 10 elementos y no necesariamente de 10 días. La respuesta de Espo fue la más corta, más limpia y probablemente la más irreversible.
El error con espacios y cuando no se van a eliminar archivos simplemente se resuelve de la manera estándar:
Versión un poco más educativa: podemos hacerlo todo si usamos awk de manera diferente. Normalmente, uso este método para pasar (devolver) variables de awk a sh. Como leemos todo el tiempo que no se puede hacer, ruego diferir: aquí está el método.
Ejemplo para archivos .tar sin problemas con respecto a los espacios en el nombre del archivo. Para probar, reemplace "rm" con el "ls".
Explicación:
ls -td *.tar
enumera todos los archivos .tar ordenados por hora. Para aplicar a todos los archivos en la carpeta actual, elimine la parte "d * .tar"awk 'NR>7...
salta las primeras 7 líneasprint "rm \"" $0 "\""
construye una línea: rm "nombre de archivo"eval
lo ejecutaComo estamos usando
rm
, ¡no usaría el comando anterior en un script! El uso más sabio es:En el caso de usar el
ls -t
comando no hará ningún daño en ejemplos tan tontos como:touch 'foo " bar'
ytouch 'hello * world'
. ¡No es que alguna vez creamos archivos con esos nombres en la vida real!Nota al margen. Si quisiéramos pasar una variable al sh de esta manera, simplemente modificaríamos la impresión:
para establecer la variable
VarName
al valor de$1
. Se pueden crear múltiples variables de una vez. Esto seVarName
convierte en una variable sh normal y puede usarse normalmente en un script o shell después. Entonces, para crear variables con awk y devolverlas al shell:fuente
¡Estoy de acuerdo en que analizar ls es malo!
Asegúrese de que solo se guarden los últimos 5 archivos en la
/srv/backups
ruta.fuente
Basado en la respuesta de Isaac Freeman, pero trabajando en cualquier directorio:
También puede establecer un prefijo, como
$PATH_TO_DELETE/testFi*
Si utiliza
*
después de laPATH
ls
salida de los nombres de archivos absolutos, al menos en "mi" bash :)fuente