94 votes

Comment trouver le fichier le plus ancien dans une arborescence de répertoires ?

Je cherche un Shell one-liner pour trouver le fichier le plus ancien dans une arborescence de répertoires.

93voto

espenhogbakk Points 2108

Cela fonctionne (mis à jour pour intégrer la suggestion de Daniel Andersson) :

find -type f -printf '%T+ %p\n' | sort | head -n 1

21voto

slhck Points 209720

Celui-ci est un peu plus portable et parce qu'il ne repose pas sur le système GNU find extension -printf Il fonctionne donc également sous BSD / OS X :

find . -type f -print0 | xargs -0 ls -ltr | head -n 1

Le seul inconvénient, c'est qu'il est quelque peu limité à la taille de l'écran. ARG_MAX (qui devrait n'est pas pertinent pour la plupart des noyaux plus récents). Ainsi, s'il y a plus de getconf ARG_MAX caractères retournés (262 144 sur mon système), il ne vous donne pas le bon résultat. Elle n'est pas non plus conforme à POSIX car -print0 y xargs -0 ne l'est pas.

D'autres solutions à ce problème sont présentées ici : Comment trouver le dernier fichier (le plus récent, le plus ancien, le plus vieux) dans un répertoire ? - Le wiki de Greg

12voto

Dennis Points 46916

Les commandes suivantes sont garanties pour fonctionner avec n'importe quel type de nom de fichier étrange :

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'utilisation d'un octet nul ( \0 ) au lieu d'un caractère de saut de ligne ( \n ) permet de s'assurer que la sortie de find sera toujours compréhensible dans le cas où l'un des noms de fichiers contient un caractère de saut de ligne.

El -z permet à sort et grep d'interpréter uniquement les octets nuls comme des caractères de fin de ligne. Puisqu'il n'y a pas d'interrupteur de ce type pour head, nous utilisons grep -m 1 à la place (une seule occurrence).

Les commandes sont classées par temps d'exécution (mesuré sur ma machine).

  • La première commande sera la plus lente car elle doit d'abord convertir le mtime de chaque fichier dans un format lisible par l'homme, puis trier ces chaînes. Le transfert vers cat évite de colorer la sortie.

  • La deuxième commande est légèrement plus rapide. Bien qu'elle effectue toujours la conversion de la date, le tri numérique ( sort -n ) les secondes écoulées depuis l'époque Unix est un peu plus rapide. sed supprime les secondes écoulées depuis l'époque Unix.

  • La dernière commande n'effectue aucune conversion et devrait être nettement plus rapide que les deux premières. La commande find elle-même n'affichera pas le mtime du fichier le plus ancien, donc stat est nécessaire.

Pages de manuel connexes : trouver - grep - sed - trier - stat

7voto

DrBeco Points 1675

Bien que la réponse acceptée et d'autres ici fassent le travail, si vous avez un très grand arbre, toutes ces réponses trieront l'ensemble des fichiers.

Le mieux serait de pouvoir simplement les lister et de garder la trace du plus ancien, sans avoir besoin de faire le moindre tri.

C'est pourquoi j'ai trouvé cette solution alternative :

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}'

J'espère que cela pourra vous être utile, même si la question est un peu ancienne.


Edit 1 : ce changement permet d'analyser les fichiers et les répertoires avec des espaces. C'est assez rapide de l'émettre dans la racine / et trouver le fichier le plus ancien.

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}'

Commande expliquée :

  • ls -lRU --time-style=long-iso "$PWD"/* liste tous les fichiers (*), format long (l), récursivement (R), sans tri (U) pour être rapide, et le pipe à awk
  • Ensuite, Awk BEGIN en mettant à zéro le compteur (facultatif pour cette question) et en fixant la date la plus ancienne oldd à aujourd'hui, au format YearMonthDay.
  • La boucle principale d'abord
    • Saisit le 6ème champ, la date, au format Année-Mois-Jour, et le change en Année-Mois-Jour (si votre ls ne s'affiche pas de cette façon, vous devrez peut-être l'affiner).
    • En utilisant la méthode récursive, il y aura des lignes d'en-tête pour tous les répertoires, sous la forme de /répertoire/ici :. Récupérez cette ligne dans la variable pat. (en remplaçant le dernier " :" par un "/"). Et met $6 à rien pour éviter d'utiliser la ligne d'en-tête comme une ligne de fichier valide.
    • si le champ $6 a un nombre valide, c'est une date. Comparez-la avec l'ancienne date oldd.
    • Est-il plus vieux ? Enregistrez alors les nouvelles valeurs pour l'ancienne date oldd et l'ancien nom de fichier oldf. BTW, oldf n'est pas seulement le 8ème champ, mais du 8ème à la fin. C'est pourquoi une boucle permet de concaténer du 8ème au NF (fin).
    • Le compte avance d'une unité
    • FINIR en imprimant le résultat

Je le dirige :

~$time ls -lRU "$PWD"/* | awk etc.

Date la plus ancienne : 19691231

Fichier : /home/.../.../backupold/.../EXAMPLES/how-to-program.txt

Total comparé : 111438

réel 0m1.135s

utilisateur 0m0.872s

sys 0m0.760s


EDIT 2 : Même concept, meilleure solution en utilisant find de regarder le temps d'accès (utiliser %T avec le premier printf para temps de modification o %C para changement de statut à la place).

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 : La commande ci-dessous utilise temps de modification et imprime également la progression incrémentale au fur et à mesure qu'il trouve des fichiers de plus en plus anciens, ce qui est utile lorsque vous avez des horodatages incorrects (comme 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}'

4voto

user1363990 Points 165

Veuillez utiliser ls - la page de manuel vous indique comment ordonner le répertoire.

ls -clt | head -n 2

Le -n 2 est pour que vous n'ayez pas le "total" dans la sortie. Si vous voulez seulement le nom du fichier.

ls -t | head -n 1

Et si vous avez besoin de la liste dans l'ordre normal (en récupérant le fichier le plus récent)

ls -tr | head -n 1

Beaucoup plus facile que d'utiliser find, beaucoup plus rapide, et plus robuste - pas besoin de se soucier des formats de noms de fichiers. Il devrait également fonctionner sur presque tous les systèmes.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X