L'analisi dell'output di nonls è affidabile .
Invece, usa findper localizzare i file e sortper ordinarli in base al timestamp. Per esempio:
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Cosa sta facendo tutto questo?
Innanzitutto, i findcomandi individuano tutti i file e le directory nella directory corrente ( .), ma non nelle sottodirectory della directory corrente ( -maxdepth 1), quindi stampano:
- Un timestamp
- Uno spazio
- Il percorso relativo al file
- Un carattere NULL
Il timestamp è importante. L' %T@identificatore di formato per si -printfsuddivide in T, che indica "Ora ultima modifica" del file (mtime) e @, che indica "Secondi dal 1970", compresi i secondi frazionari.
Lo spazio è semplicemente un delimitatore arbitrario. Il percorso completo del file è in modo che possiamo fare riferimento ad esso in un secondo momento, e il carattere NULL è un terminatore perché è un carattere illegale in un nome di file e quindi ci fa sapere con certezza che abbiamo raggiunto la fine del percorso per il file.
Ho incluso in 2>/dev/nullmodo tale da escludere i file ai quali l'utente non ha il permesso di accedere, ma i messaggi di errore che li escludono vengono eliminati.
Il risultato del findcomando è un elenco di tutte le directory nella directory corrente. L'elenco viene reindirizzato a sortcui viene richiesto di:
-z Considera NULL come carattere di terminazione di riga anziché come nuova riga.
-n Ordina numericamente
Poiché i secondi, dal 1970, salgono sempre, vogliamo il file il cui timestamp era il numero più piccolo. Il primo risultato da sortsarà la riga contenente il timestamp numerato più piccolo. Non resta che estrarre il nome del file.
I risultati della pipeline find, sortvengono passati attraverso la sostituzione del processo nel punto in whilecui viene letto come se fosse un file su stdin. whilea sua volta invoca readper elaborare l'input.
Nel contesto di readimpostare la IFSvariabile su nulla, il che significa che gli spazi bianchi non verranno interpretati in modo inappropriato come delimitatore. readviene detto -r, che disabilita l'espansione di fuga, e -d $'\0', il che rende il delimitatore NULL end-of-line, abbinando l'uscita dal nostro find, sortpipeline.
Il primo blocco di dati, che rappresenta il percorso del file più vecchio preceduto dal suo timestamp e da uno spazio, viene letto nella variabile line. Successivamente, la sostituzione dei parametri viene utilizzata con l'espressione #*, che sostituisce semplicemente tutti i caratteri dall'inizio della stringa fino al primo spazio, incluso lo spazio, con nulla. Questo elimina il timestamp di modifica, lasciando solo il percorso completo del file.
A questo punto il nome del file è archiviato $filee puoi fare tutto ciò che ti piace. Quando hai finito di fare qualcosa con $filel' whileistruzione, il ciclo readverrà eseguito e il comando verrà eseguito di nuovo, estraendo il blocco successivo e il nome del file successivo.
Non c'è un modo più semplice?
No. I modi più semplici sono corretti.
Se si utilizza ls -te si reindirizza heado tail(o altro ) si interromperanno i file con le nuove righe nei nomi dei file. Se mv $(anything)poi i file con spazi bianchi nel nome causeranno la rottura. Se mv "$(anything)"poi file con nuove righe finali nel nome causeranno la rottura. Se readsenza -d $'\0'Allora ti rompi il file con spazi nei loro nomi.
Forse in casi specifici sai per certo che è sufficiente un modo più semplice, ma non dovresti mai scrivere ipotesi del genere negli script se riesci a evitare di farlo.
Soluzione
#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Chiama come:
move-oldest /mnt/backup/ /var/log/foo/ 20
Per spostare i 20 file più vecchi da /var/log/foo/a /mnt/backup/.
Nota che sto includendo file e directory. Per i file solo aggiungere -type falla findinvocazione.
Grazie
Grazie a enzotib e Павел Танков per i miglioramenti a questa risposta.