risposta-alla-domanda-sullo-sviluppo-web-bd.com

Elimina tutti i file tranne i più recenti 3 in bash script

Domanda: Come si eliminano tutti i file in una directory tranne i più recenti 3?

Trovare i nuovi 3 file è semplice:

ls -t | head -3

Ma ho bisogno di trovare tutti i file tranne i più recenti 3 file. Come faccio a farlo, e come faccio a eliminare questi file nella stessa riga senza fare un loop inutile per quello?

Sto usando Debian Wheezy e gli script di bash per questo.

13
bytecode77

Questo elencherà tutti i file tranne i tre più recenti:

ls -t | tail -n +4

Questo cancellerà quei file:

ls -t | tail -n +4 | xargs rm --

Questo elencherà anche i dotfile:

ls -At | tail -n +4

ed elimina con dotfiles:

ls -At | tail -n +4 | xargs rm --

Ma attenzione: l'analisi di ls può essere pericolosa quando i nomi dei file contengono caratteri divertenti come le nuove o gli spazi. Se si è certi che i nomi dei file non contengano caratteri divertenti, l'analisi di ls è abbastanza sicura, ancor più se si tratta di uno script solo una volta.

Se stai sviluppando uno script per uso ripetuto, non dovresti assolutamente analizzare l'output di ls e utilizzare i metodi descritti qui: http://mywiki.wooledge.org/ParsingLs

37
lesmana

Quanto segue sembra un po 'complicato, ma è molto prudente per essere corretto, anche con nomi di file insoliti o intenzionalmente dannosi. Sfortunatamente, richiede GNU strumenti:

count=0
while IFS= read -r -d ' ' && IFS= read -r -d '' filename; do
  (( ++count > 3 )) && printf '%s\0' "$filename"
done < <(find . -maxdepth 1 -type f -printf '%[email protected] %P\0' | sort -g -z) \
     | xargs -0 rm -f --

Spiegando come funziona:

  • Trova emette <mtime> <filename><NUL> per ogni file nella directory corrente.
  • sort -g -z esegue un ordinamento numerico generale (a virgola mobile, anziché intero) basato sulla prima colonna (volte) con le righe separate da NUL.
  • Il primo read nel ciclo while rimuove il mtime (non più necessario dopo che sort è terminato).
  • Il secondo read nel ciclo while legge il nome del file (in esecuzione fino al NUL).
  • Il loop incrementa e quindi controlla un contatore; se lo stato del contatore indica che abbiamo superato il salto iniziale, stampiamo il nome del file, delimitato da un NUL.
  • xargs -0 quindi aggiunge quel nome di file all'elenco di argv che sta raccogliendo per invocare rm con.
9
Charles Duffy
ls -t | tail -n +4 | xargs -I {} rm {}

Se vuoi un 1 liner

6
Michael Ballent

Questa è una combinazione di risposta del ceving e di anubhava. Entrambe le soluzioni non funzionano per me. Poiché stavo cercando uno script che dovrebbe essere eseguito ogni giorno per il backup dei file in un archivio, volevo evitare problemi con ls (qualcuno avrebbe potuto salvare qualche divertente nome file nella mia cartella di salvataggio). Così ho modificato le soluzioni citate per adattarle alle mie esigenze. La soluzzione di Ceving cancella i tre file più recenti, non quello di cui avevo bisogno e mi è stato chiesto. 

La mia soluzione cancella tutti i file, tranne i tre file più recenti.

find . -type f -printf '%[email protected]\t%p\n' |
sort -t $'\t' -g | 
head -n -3 | 
cut -d $'\t' -f 2- |
xargs rm

Qualche spiegazione: 

find elenca tutti i file (non le directory) nella cartella corrente. Sono stampati con timestamp.
sort ordina le righe in base al timestamp (più vecchio in alto).
head stampa le prime righe, fino alle ultime 3 righe.
cut rimuove i timestamp.
xargs esegue rm per ogni file selezionato.

Per verificare la mia soluzione:

(
touch -d "6 days ago" test_6_days_old
touch -d "7 days ago" test_7_days_old
touch -d "8 days ago" test_8_days_old
touch -d "9 days ago" test_9_days_old
touch -d "10 days ago" test_10_days_old
)

Questo crea 5 file con data e ora differenti nella cartella corrente. Esegui prima questo e il codice per l'eliminazione per testare il codice.

4
flohall

In zsh:

rm /files/to/delete/*(Om[1,-4])

Se si desidera includere dotfile , sostituire la parte tra parentesi con (Om[1,-4]D).

Penso che funzioni correttamente con caratteri arbitrari nei nomi dei file (appena controllato con newline).

Spiegazione: le parentesi contengono i qualificatori Glob. Osignifica "ordina per, decrescente", msignifica mtime (vedi man zshexpn per le altre chiavi di ordinamento - manpage di grandi dimensioni; cerca "essere ordinato"). [1,-4] restituisce solo le corrispondenze all'indice 1 basato su uno (last + 1 - 4) (notare -4 per l'eliminazione di tutti tranne 3).

1
FunctorSalad

Non utilizzare ls -t in quanto non sicuro per i nomi di file che potrebbero contenere spazi bianchi o caratteri speciali glob.

Puoi farlo usando tutte le utility basate su gnu per eliminare tutti i 3 file più nuovi nella directory corrente:

find . -maxdepth 1 -type f -printf '%[email protected]\t%p\0' |
sort -z -nrk1 |
tail -z -n +4 |
cut -z -f2- |
xargs -0 rm -f --
1
anubhava

Questo usa find invece di ls con una trasformazione di Schwartzian

find . -type f -printf '%[email protected]\t%p\n' |
sort -t $'\t' -g |
tail -3 |
cut -d $'\t' -f 2-

find cerca i file e li decora con un timestamp e utilizza il tabulatore per separare i due valori. sort divide l'input dal tabulatore ed esegue un ordinamento numerico generale, che ordina correttamente i numeri in virgola mobile. tail dovrebbe essere ovvio e cut undecorates.

Il problema con le decorazioni in generale è trovare un delimitatore adatto, che non fa parte dell'input, i nomi dei file. Questo answer usa il carattere NULL.

0
ceving
ls -t | tail -n +4 | xargs -I {} rm {}

La risposta di Michael Ballent funziona meglio come 

ls -t | tail -n +4 | xargs rm --

lanciami errore se ho meno di 3 file

0
klesk44