Sto cercando di mantenere gestibile una directory piena di file di registro. Di notte, voglio eliminare tutti tranne i 10 più recenti. Come posso farlo in un singolo comando?
Sto cercando di mantenere gestibile una directory piena di file di registro. Di notte, voglio eliminare tutti tranne i 10 più recenti. Come posso farlo in un singolo comando?
Risposte:
Per una soluzione portatile e affidabile, prova questo:
ls -1tr | head -n -10 | xargs -d '\n' rm -f --
La tail -n -10
sintassi in una delle altre risposte non sembra funzionare ovunque (cioè, non sui miei sistemi RHEL5).
E l'utilizzo $()
o ``
sulla riga di comando rm
corre il rischio di
xargs
risolve entrambi questi problemi perché capirà automaticamente quanti argomenti può passare entro il limite di caratteri e con -d '\n'
esso si dividerà solo al limite della linea dell'input. Tecnicamente questo può ancora causare problemi per i nomi di file con una nuova riga al loro interno, ma è molto meno comune dei nomi di file con spazi, e l'unico modo per aggirare le nuove righe sarebbe molto più complicato, probabilmente coinvolgendo almeno awk, se non perl.
Se non hai xargs (vecchi sistemi AIX, forse?) Potresti farlo diventare un ciclo:
ls -1tr | head -n -10 | while IFS= read -r f; do
rm -f "$f"
done
Questo sarà un po 'più lento perché genera un rm
file separato per ogni file, ma eviterà comunque le avvertenze 1 e 2 sopra (ma soffre ancora di nuove righe nei nomi dei file).
head(1)
pagina man:-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
e xargs
dare errori. Le opzioni corrette sono head -n 10
e xargs rm -f
. Il comando completo èls -1tr ./ | head -n 10 | xargs rm -rf
ls -1tr
è il numero UNO non la lettera "L".
Il codice che vorresti includere nel tuo script è
rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)
L' -1
opzione (uno numerico) stampa ogni file su una sola riga, per sicurezza. L' -f
opzione per rm
dirgli di ignorare i file inesistenti per quando ls
non restituisce nulla.
/path/to/your/logs
è solo fatto per te per inserire il percorso corretto. Per trovare il bug eseguire le parti ls -t /path/...
e ls -t /path/... | tail -n +11
da solo e verificare se il risultato è corretto.
+
prima del numero. Non tail
stampa gli ultimi n elementi ma tutti gli elementi a partire dall'ennesimo. Ho effettuato nuovamente il check-in e il comando dovrebbe sicuramente rimuovere solo gli elementi più vecchi di 10 file più recenti in ogni circostanza.
ls
stampa un nome di file, non un percorso, quindi rm -f
Apparentemente analizzare ls
è male .
Se ogni file viene creato quotidianamente e si desidera mantenere i file creati negli ultimi 10 giorni è possibile:
find /path/to/files -mtime 10 -delete
O se ogni file viene creato in modo arbitrario:
find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
-mtime 10 -delete
elimina solo i file modificati esattamente 10 giorni fa. I file meno recenti non verranno eliminati. -mtime +11 -delete
eliminerà i file che sono stati modificati 11 o più giorni fa, lasciando gli ultimi 10 giorni dei file di registro.
%p
invece di %P
sia necessario in printf
modo che i file ottenuti abbiano il percorso completo. Altrimenti il rm -rf
non funziona.
Ho modificato un po ' l'approccio di Isaac .
Ora funziona con un percorso desiderato:
ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
Da qui :
#! /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
In Bash puoi provare:
ls -1t | (i=0; while read f; do
if [ $i -lt 10 ]; then
((i++))
continue
else
rm -f "$f"
fi
done)
Questo salta le 10 più recenti elimina il resto. logrotate potrebbe essere migliore, voglio solo correggere le risposte errate relative alla shell.
non sono sicuro che questo possa aiutare nessuno, ma per evitare potenziali problemi con i personaggi traballanti ho usato solo ls
per eseguire l'ordinamento, ma quindi utilizzare i file inode
per l'eliminazione.
Per la directory corrente, questo mantiene i 10 file più recenti in base al tempo di modifica.
ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;
Avevo bisogno di una soluzione elegante per la busybox (router), tutte le soluzioni xargs o array erano inutili per me - nessun comando disponibile lì. find and mtime non è la risposta corretta in quanto stiamo parlando di 10 articoli e non necessariamente di 10 giorni. La risposta di Espo è stata la più breve, pulita e probabilmente la più inversa.
Errore con spazi e quando nessun file deve essere eliminato sono entrambi risolti semplicemente nel modo standard:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
Versione un po 'più educativa: possiamo fare tutto se usiamo awk in modo diverso. Normalmente, uso questo metodo per passare (restituire) variabili da awk a sh. Mentre leggiamo tutto il tempo che non si può fare, mi permetto di dissentire: ecco il metodo.
Esempio di file .tar senza problemi relativi agli spazi nel nome file. Per provare, sostituire "rm" con "ls".
eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')
Spiegazione:
ls -td *.tar
elenca tutti i file .tar ordinati per ora. Per applicare a tutti i file nella cartella corrente, rimuovere la parte "d * .tar"
awk 'NR>7...
salta le prime 7 righe
print "rm \"" $0 "\""
costruisce una riga: rm "nome file"
eval
lo esegue
Dal momento che stiamo usando rm
, non vorrei usare il comando sopra in uno script! L'uso più saggio è:
(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))
Nel caso di usare il ls -t
comando non farà alcun danno su esempi sciocchi come: touch 'foo " bar'
e touch 'hello * world'
. Non che abbiamo mai creato file con tali nomi nella vita reale!
Nota a margine. Se volessimo passare una variabile a sh in questo modo, modificheremmo semplicemente la stampa:
print "VarName="$1
per impostare la variabile VarName
sul valore di $1
. È possibile creare più variabili in una volta sola. Questa VarName
diventa una normale variabile sh e successivamente può essere normalmente utilizzata in uno script o in una shell. Quindi, per creare variabili con awk e restituirle alla shell:
eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"
Basato sulla risposta di Isaac Freeman, ma lavorando su qualsiasi directory:
ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --
Puoi anche impostare un prefisso, ad esempio $PATH_TO_DELETE/testFi*
Se usi *
dopo l' PATH
ls
output verranno generati nomi di file assoluti, almeno in "mio" bash :)