Todo el guión.

Todo el guión.

Tengo una secuencia de comandos en la que quiero limpiar archivos antiguos cuando superan el límite establecido.

Tengo este comando:

/bin/rm -f `/bin/ls -t $bkup_p/mysql.daily/*  2> /dev/null | /bin/awk 'NR>'5`

que funciona y como podría haber un espacio en $bkup_p, intenté cambiarlo a

/bin/rm -f `/bin/ls -t "$bkup_p/mysql.daily/*"  2> /dev/null | /bin/awk 'NR>'5`

Pero eso no funciona. No muestra los archivos en cuestión, simplemente está vacío.

Respuesta1

Bash no es muy útil cuando se trata de filtrar archivos en el momento de la modificación y ejecutar comandos sobre ellos.

Recomendaría probar z-shell en su lugar, así que primero ejecute zshy luego

rm -i -- "$bkup_p"/mysql.daily/*(DN.Om[1,5])

Esto elimina los cinco archivos simples más antiguos en el directorio dado, lo que sospecho es probablemente lo que estás tratando de lograr. Obviamente, al final del día, cambie rm -ia rm -fsi es necesario.

Para eliminar cinco archivos más nuevos, haga

rm -i -- "$bkup_p"/mysql.daily/*(DN.om[1,5])

Ahora, cómo funciona. Todo lo que contiene ()son los llamados calificadores globales, que básicamente filtran archivos según sus necesidades:

  • Dincluir archivos de puntos (aquellos que comienzan con .)
  • Nno reportar error si no hay coincidencias
  • .selecciona sólo archivos simples
  • omordena según el momento de modificación ( Omordena en orden inverso)
  • [1,5]selecciona sólo cinco archivos de la lista

Creo que todo eso debería funcionar incluso con caracteres especiales en los nombres de archivos (espacios, nuevas líneas, etc.)

Respuesta2

Como han dicho otros, no deberías usarlo lspara eso. Pero si tu entorno no tiene nada más sensato y túconoce las advertencias, túpodríasalirse con la suya, si los nombres de sus archivos sonbastante agradable: sin espacios en blanco ni caracteres no imprimibles, y especialmente nada que el shell interprete como un carácter global (al menos ?*[]).

Sería mejor usar algo como Perl para ordenar los archivos o poner las fechas en los nombres de los archivos para que pueda depender del orden global.


Dicho esto: en su segundo fragmento, el asterisco está entre comillas, por lo que no se produce globbing. Vería un error que indica que some dir/mysql.daily/*no existe, pero no lo ve porque redirigió el resultado del error.

Los nombres de archivos con espacios le darán el problema de que la salida de la sustitución del comando se divide a lo largo de los espacios, por lo que some dir/foose le dará algo como rmen dos partes: somey dir/foo. Esperemos que ninguno de ellos exista, ya que serían eliminados.

Si solo el nombre de su directorio contiene espacios, puede cdingresarlo primero. O configúrelo IFSpara que contenga solo una nueva línea (en lugar del espacio predeterminado, tabulación, nueva línea). Mencionaste un NAS, por lo que es probable que tengas una caja ocupada. Esto debería funcionar tanto en ocupadobox como en bash, e imprimir los nombres de los archivos, uno por línea:

IFS=$'\n'
printf "%s\n" $( ls -t "$bkup_p/mysql.daily/"* | tail -n +6 )

Reemplace printf "%s\n"con rmpara eliminar los archivos, si está seguro de que funciona correctamente.

Respuesta3

Recomendaría no analizar la salida de lsya que no formatea correctamente la salida para canalizarla a un nuevo comando y tiene problemas de portabilidad.
Yo intentaría algo como esto en su lugar

find $bkup_p/mysql.daily/ -type f -a -mtime +7 -a -name "*.sql" -a -exec rm -f {} +

nota:

  • "*.sql"cambie esto según sea necesario
  • -mtime +7significa *si este archivo se modificó hace más (+) de 7 días, obviamente cámbielo según sea necesario también

En el caso de que quieras asegurarte de tener siempre los 10 archivos más nuevos, pase lo que pase (y tienes GNU find), puedes intentarlo.

find $bkup_p/mysql.daily/ -maxdepth 1 -type f -a -printf "%T+\t%p\n" | sort -r | sed -n '10,$p' | awk '{print $2}' | xargs rm -f

Para obtener más información sobre cómo formatear findla salida con el -printfoperador, consulte

man find | less '+/^\s*-printf'

Respuesta4

El problema de que *no funciona es porque cuando lo citas no se expande.

Compare los resultados de esto: echo *con esto: echo "*".


La solución sólida es utilizar una matriz, un archivo en cada posición de la matriz.

Si tienes la suerte y encuentras soporte para esto -printf:

dir="$bkup_p/mysql.daily/"

find "$dir" -printf '%T@:%i\n'

Se evitarán todos los nombres de archivos. Cada archivo será una línea. Y una línea de solo números, un punto opcional y dos puntos.

Eso se podría ordenar fácilmente:

find "$dir" -printf '%T@:%i\n' | sort -rn

La matriz más simple son los argumentos posicionales.
Esto configurará todos los archivos en el directorio en la lista de argumentos posicionales:

set -f; set -- $(find "$dir" -printf '%T@:%i\n' | sort -n )

El conjunto -f evitará la expansión de cualquier nombre de archivo que también sea un comodín *. Para evitar (eliminar) los primeros 6 nombres de archivos, solo shiftellos:

shift 5

Y haz lo que necesites con los archivos restantes:

for    f
do     rm -f "$(find "$dir" -inum "${f#*:}")"
done

Todo el guión.

El script completo (robusto para cualquier nombre de archivo) se convierte en:

dir="$bkup_p/mysql.daily/"

set -f
set -- $(find "$dir" -type f -printf '%T@:%i\n'|sort -rn)
shift 5

for    f
do     rm -f "$(find "$dir" -inum "${f#*:}")"
done

información relacionada