
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 zsh
y 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 -i
a rm -f
si 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:
D
incluir archivos de puntos (aquellos que comienzan con.
)N
no reportar error si no hay coincidencias.
selecciona sólo archivos simplesom
ordena según el momento de modificación (Om
ordena 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 ls
para 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/foo
se le dará algo como rm
en dos partes: some
y dir/foo
. Esperemos que ninguno de ellos exista, ya que serían eliminados.
Si solo el nombre de su directorio contiene espacios, puede cd
ingresarlo primero. O configúrelo IFS
para 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 rm
para eliminar los archivos, si está seguro de que funciona correctamente.
Respuesta3
Recomendaría no analizar la salida de ls
ya 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 +7
significa *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 find
la salida con el -printf
operador, 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 shift
ellos:
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