¿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?

usuario75814
fuente
3
echar un vistazo a logrotate
knittl
@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

  1. división de nombres de archivos con espacios en blanco, y
  2. 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).

Isaac Freeman
fuente
@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.

Daniel Böhmer
fuente
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
Jürgen Steinblock
14

Aparentemente analizar lses malo .

Si cada archivo se crea a diario y desea mantener los archivos creados en los últimos 10 días, puede hacer lo siguiente:

find /path/to/files -mtime 10 -delete

O si cada archivo se crea arbitrariamente:

find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
El único
fuente
55
-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.

BillThor
fuente
2

He modificado el enfoque de Isaac un poco.

Ahora funciona con una ruta deseada:

ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
Sebastian U.
fuente
1

Desde aquí :

#! /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
manoflinux
fuente
1
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.

Hauke ​​Laging
fuente
1

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.

ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;

Flo Woo
fuente
0

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".

eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')

Explicación:

ls -td *.tarenumera 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íneas

print "rm \"" $0 "\"" construye una línea: rm "nombre de archivo"

eval lo ejecuta

Como estamos usando rm, ¡no usaría el comando anterior en un script! El uso más sabio es:

(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))

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:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"
Pila
fuente
0

¡Estoy de acuerdo en que analizar ls es malo!

Asegúrese de que solo se guarden los últimos 5 archivos en la /srv/backupsruta.

find /srv/backups -maxdepth 1 -type f -printf '%Ts\t%P\n' \
    | sort -rn \
    | tail -n +6 \
    | cut -f2- \
    | xargs -r r
h0tw1r3
fuente
0

Basado en la respuesta de Isaac Freeman, pero trabajando en cualquier directorio:

ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --

También puede establecer un prefijo, como $PATH_TO_DELETE/testFi*

Si utiliza *después de la PATH lssalida de los nombres de archivos absolutos, al menos en "mi" bash :)

Bis
fuente