Sto cercando una shell one-liner per trovare il file più vecchio in un albero di directory.
Sto cercando una shell one-liner per trovare il file più vecchio in un albero di directory.
Risposte:
Funziona (aggiornato per includere il suggerimento di Daniel Andersson):
find -type f -printf '%T+ %p\n' | sort | head -n 1
find
è vuota a causa del fatto che ho nome file contiene newline.
Questo è un po 'più portatile e perché non si basa su GNU find
estensione -printf
, quindi funziona su BSD / OS X così:
find . -type f -print0 | xargs -0 ls -ltr | head -n 1
L'unico aspetto negativo qui è che è in qualche modo limitato alla dimensione di ARG_MAX
(che dovrebbe essere irrilevante per la maggior parte dei kernel più recenti). Quindi, se vengono getconf ARG_MAX
restituiti più di caratteri (262.144 sul mio sistema), non si ottiene il risultato corretto. Inoltre, non è conforme a POSIX perché -print0
e xargs -0
non lo è.
Alcune ulteriori soluzioni a questo problema sono delineate qui: Come posso trovare il file più recente (più recente, più vecchio, più vecchio) in una directory? - Greg's Wiki
xargs: ls: terminated by signal 13
errore come effetto collaterale. Immagino che sia SIGPIPE. Non ho idea del motivo per cui non ottengo un errore simile quando installo l'output di sort per dirigere nella mia soluzione.
head
comando che si chiude una volta che ha letto una riga e quindi "rompe" la pipe, credo. Non si ottiene l'errore perché sort
non sembra lamentarsene, ma ls
nell'altro caso.
xargs
devono invocare ls
più di una volta. In tal caso, gli output ordinati di tali invocazioni multiple finiscono per essere concatenati quando dovrebbero essere uniti.
ls
e controllare il file più vecchio, la soluzione probabilmente supererà il limite di lunghezza della riga di comando, causando ls
l'invocazione più volte. Avrai la risposta sbagliata ma non lo saprai mai.
I seguenti comandi comandi sono garantiti per funzionare con qualsiasi tipo di nomi di file strani:
find -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat
find -type f -printf "%T@ %T+ %p\0" | \
sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'
stat -c "%y %n" "$(find -type f -printf "%T@ %p\0" | \
sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"
L'uso di un byte null ( \0
) anziché un carattere di avanzamento riga ( \n
) assicura che l'output di find sia ancora comprensibile nel caso in cui uno dei nomi di file contenga un carattere di avanzamento riga.
L' -z
opzione fa in modo che sia l'ordinamento che il grep interpretino solo i byte null come caratteri di fine riga. Dal momento che non esiste un tale interruttore per head, usiamo grep -m 1
invece (solo un'occorrenza).
I comandi sono ordinati in base al tempo di esecuzione (misurato sulla mia macchina).
Il primo comando sarà il più lento poiché deve prima convertire ogni mtime di ogni file in un formato leggibile dall'uomo e quindi ordinare quelle stringhe. Il piping su cat evita di colorare l'output.
Il secondo comando è leggermente più veloce. Mentre esegue ancora la conversione della data, l'ordinamento numerico ( sort -n
) dei secondi trascorsi dall'epoca di Unix è un po 'più veloce. sed cancella i secondi dall'epoca di Unix.
L'ultimo comando non esegue alcuna conversione e dovrebbe essere significativamente più veloce dei primi due. Il comando find stesso non visualizzerà il mtime del file più vecchio, quindi è necessario stat.
Anche se la risposta accettata e altri qui fanno il lavoro, se hai un albero molto grande, tutti ordineranno l'intero gruppo di file.
Meglio sarebbe se potessimo semplicemente elencarli e tenere traccia dei più vecchi, senza la necessità di ordinare affatto.
Ecco perché mi è venuta in mente questa soluzione alternativa:
ls -lRU $PWD/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($1,0,1)=="/") { pat=substr($1,0,length($0)-1)"/"; }; if( $6 != "") {if ( $6 < oldd ) { oldd=$6; oldf=pat$8; }; print $6, pat$8; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
Spero possa essere di qualche aiuto, anche se la domanda è un po 'vecchia.
Modifica 1: queste modifiche consentono di analizzare file e directory con spazi. È abbastanza veloce da emetterlo nella radice /
e trovare il file più vecchio di sempre.
ls -lRU --time-style=long-iso "$PWD"/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($0,0,1)=="/") { pat=substr($0,0,length($0)-1)"/"; $6="" }; if( $6 ~ /^[0-9]+$/) {if ( $6 < oldd ) { oldd=$6; oldf=$8; for(i=9; i<=NF; i++) oldf=oldf $i; oldf=pat oldf; }; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
Comando spiegato:
Eseguendolo:
~ $ time ls -lRU "$ PWD" / * | awk ecc.
Data più antica: 19691231
File: /home/.../.../backupold/.../EXAMPLES/how-to-program.txt
Totale rispetto: 111438
0m1.135s reali
utente 0m0.872s
sys 0m0.760s
EDIT 2: Stesso concetto, soluzione migliore usando find
per guardare il tempo di accesso (usare invece %T
il primo printf
per il tempo di modifica o %C
per il cambio di stato ).
find . -wholename "*" -type f -printf "%AY%Am%Ad %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
EDIT 3: Il comando muggito utilizza il tempo di modifica e stampa anche i progressi incrementali quando trova file più vecchi e più vecchi, il che è utile quando si hanno dei timestamp errati (come 1970-01-01):
find . -wholename "*" -type f -printf "%TY%Tm%Td %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; print oldd " " oldf; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
ls
è negativo per gli script poiché l'output non è destinato alle macchine, la formattazione dell'output varia a seconda delle implementazioni. Come hai già detto, find
è buono per gli script, ma potrebbe anche essere utile aggiungere tali informazioni prima di parlare delle ls
soluzioni.
Per favore usa ls - la pagina man ti dice come ordinare la directory.
ls -clt | head -n 2
-N 2 è quindi non si ottiene il "totale" nell'output. Se vuoi solo il nome del file.
ls -t | head -n 1
E se hai bisogno dell'elenco nell'ordine normale (ottenere il file più recente)
ls -tr | head -n 1
Molto più facile dell'utilizzo di find, molto più veloce e più robusto - non devi preoccuparti dei formati di denominazione dei file. Dovrebbe funzionare anche su quasi tutti i sistemi.
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
sort -n
.
Sembra che per "più vecchio" la maggior parte delle persone abbia ipotizzato che intendessi "tempo di modifica più vecchio". Questo è probabilmente corretto, secondo l'interpretazione più rigorosa di "più vecchio", ma nel caso volessi quello con il tempo di accesso più vecchio , modificherei così la risposta migliore:
find -type f -printf '%A+ %p\n' | sort | head -n 1
Notare il %A+
.
set $(find /search/dirname -type f -printf '%T+ %h/%f\n' | sort | head -n 1) && echo $2
find ./search/dirname -type f -printf '%T+ %h/%f\n'
stampa le date e i nomi dei file in due colonne.sort | head -n1
mantiene la riga corrispondente al file più vecchio.echo $2
visualizza la seconda colonna, ovvero il nome del file.
find -type f -printf '%T+ %p\n' | sort | head -1