Come posso eliminare tutti i file tranne 10 più recenti in Linux?


49

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?


3
dai un'occhiata a logrotate
knittl


@michael Come può essere il mio duplicato di quello, se la mia discussione è stata creata per prima?
dev4life,

@ovaherenow "tuoi"? Non vedo il tuo nome su nessuna delle due domande, quindi non sono sicuro di cosa tu voglia dire. Il mio commento non indica nemmeno quale sia un duplicato dell'altro. Ma non importa: è solo un commento, con un link. Potrebbe essere aggiunto a una o entrambe le domande. Spetta a qualcun altro - un amministratore - contrassegnare l'uno o l'altro come duplicato. Non era prevista alcuna sventura sventura e nessuno dovrebbe essere percepito. Più importante di quale sia stata la prima domanda, è quale domanda ha la risposta migliore - di nuovo, il collegamento facilita questa discussione, non determina la risposta.
michael

@michael wow. È davvero strano. Questo thread è stato aperto da me, ma ovviamente non è il mio nome utente. Ma mi viene notificato questo thread.
dev4life,

Risposte:


58

Per una soluzione portatile e affidabile, prova questo:

ls -1tr | head -n -10 | xargs -d '\n' rm -f --

La tail -n -10sintassi in una delle altre risposte non sembra funzionare ovunque (cioè, non sui miei sistemi RHEL5).

E l'utilizzo $()o ``sulla riga di comando rmcorre il rischio di

  1. dividere i nomi dei file con spazi bianchi e
  2. superando il limite massimo di caratteri della riga di comando.

xargsrisolve 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 rmfile separato per ogni file, ma eviterà comunque le avvertenze 1 e 2 sopra (ma soffre ancora di nuove righe nei nomi dei file).


@HaukeLaging: Dalla 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
Isaac Freeman,

Su Solaris 10 heade xargsdare errori. Le opzioni corrette sono head -n 10e xargs rm -f. Il comando completo èls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov il

1
Nota che ls -1trè il numero UNO non la lettera "L".
utente Linux shonky

1
Va anche notato che le nuove righe sono caratteri rari ma validi nei nomi di file ext. Ciò significa che se hai i file "that", e "that \ nthing", se questo script colpisce "that \ nthing", eliminerà "that", errore quando non riuscirà a cancellare "cosa" e lascerà "that \ nthing" (il target previsto) inalterato.
Synthead

1
@IsaacFreeman Ho cancellato il mio cattivo consiglio, evviva.
Walf

20

Il codice che vorresti includere nel tuo script è

 rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)

L' -1opzione (uno numerico) stampa ogni file su una sola riga, per sicurezza. L' -fopzione per rmdirgli di ignorare i file inesistenti per quando lsnon restituisce nulla.


Poster originale, qui. Ho provato questo comando, ma non funziona - Ottengo l'errore "rm: operando mancante"
user75814

2
Hai usato il percorso corretto? Ovviamente /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 +11da solo e verificare se il risultato è corretto.
Daniel Böhmer,

1
@HaukeLaging Sono abbastanza sicuro che tu abbia sbagliato qualcosa. Fai attenzione a +prima del numero. Non tailstampa 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.
Daniel Böhmer,

@halo Anzi, scusa.
Hauke ​​Laging,

2
Penso che la tua risposta sia molto pericolosa lsstampa un nome di file, non un percorso, quindi rm -f
proverai

14

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

5
-mtime 10 -deleteelimina solo i file modificati esattamente 10 giorni fa. I file meno recenti non verranno eliminati. -mtime +11 -deleteeliminerà i file che sono stati modificati 11 o più giorni fa, lasciando gli ultimi 10 giorni dei file di registro.
Markus,

1
Penso che l'uso di %pinvece di %Psia necessario in printfmodo che i file ottenuti abbiano il percorso completo. Altrimenti il rm -rfnon funziona.
jalopaba,

9

Uno strumento come logrotate fa questo per te. Rende molto più semplice la gestione dei registri. È inoltre possibile includere ulteriori routine di pulizia, come indicato dall'alone.



1

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

1
Grazie per aver fornito questa risposta. Sebbene fornire codice come questo sia utile, aiuta anche a fornire alcune informazioni aggiuntive su come potrebbe essere utilizzato o su cosa fa il codice. Puoi utilizzare il pulsante Modifica per apportare miglioramenti futuri al tuo post.
Pensa

1

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.


1

non sono sicuro che questo possa aiutare nessuno, ma per evitare potenziali problemi con i personaggi traballanti ho usato solo lsper eseguire l'ordinamento, ma quindi utilizzare i file inodeper 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 {} \;


0

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 *.tarelenca 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 -tcomando 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 VarNamesul valore di $1. È possibile creare più variabili in una volta sola. Questa VarNamediventa 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"

0

Sono d'accordo che analizzare ls è male!

Assicurarsi che nel /srv/backupspercorso siano mantenuti solo gli ultimi 5 file .

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

0

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 lsoutput verranno generati nomi di file assoluti, almeno in "mio" bash :)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.