web-dev-qa-db-fra.com

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

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

70
Marius Gedminas

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

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

Celui-ci est un peu plus portable et, comme il ne repose pas sur le 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 est qu’il est quelque peu limité à la taille de ARG_MAX (qui devrait n’est pas pertinent pour la plupart des nouveaux noyaux). Ainsi, s'il y a plus de getconf ARG_MAX caractères renvoyés (262 144 sur mon système), le résultat n'est pas correct. Ce n'est pas non plus compatible POSIX car -print0 et xargs -0 ne le sont pas.

Quelques solutions supplémentaires à ce problème sont décrites ici: Comment puis-je trouver le fichier le plus récent (le plus récent, le plus ancien, le plus ancien) dans un répertoire? - Wiki de Greg

11
slhck

Les commandes suivantes sont garanties pour fonctionner avec tout 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) garantit que la sortie de find sera toujours compréhensible au cas où l'un des noms de fichier contiendrait un caractère de saut de ligne.

Le commutateur -z permet à la fois à sort et à grep d'interpréter uniquement les octets nuls en tant que caractères de fin de ligne. Comme il n’existe pas de commutateur de ce type pour head, nous utilisons plutôt grep -m 1 (une seule occurrence).

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

  • La première commande sera la plus lente car elle doit d'abord convertir chaque fichier mtime en un format lisible par l'homme, puis trier ces chaînes. Piping to Cat évite de colorer la sortie.

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

  • La dernière commande ne fait aucune conversion et devrait être nettement plus rapide que les deux premières. La commande de recherche elle-même n’affiche pas l’heure mtime du fichier le plus ancien. Par conséquent, stat est nécessaire.

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

11
Dennis

Bien que la réponse acceptée et d’autres ici fonctionnent, si vous avez un très grand arbre, ils trieront l’ensemble des fichiers.

Ce serait mieux si nous pouvions simplement les énumérer et garder une trace des plus anciens, sans qu'il soit nécessaire de les trier.

C'est pourquoi j'ai proposé 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 aider, même si la question est un peu ancienne.


Éditer 1: ces modifications permettent d'analyser des fichiers et des répertoires avec des espaces. Il est assez rapide pour le publier à la racine / et trouver le fichier le plus ancien jamais créé.

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 = iso long "$ PWD"/* liste tous les fichiers (*), format long (l), récursivement (R), sans trier (U) pour être rapide, et le diriger vers awk
  • Awk puis BEGIN en remettant à zéro le compteur (facultatif pour cette question) et en définissant la date la plus ancienne par aujourd'hui, formatez YearMonthDay.
  • La boucle principale en premier
    • Récupère le 6ème champ, la date, le format Année-Mois-Jour et remplacez-le par YearMonthDay (si votre ls ne s'affiche pas de cette façon, vous devrez peut-être le régler avec précision).
    • En utilisant récursif, il y aura des lignes d’en-tête pour tous les répertoires, sous la forme de/directory/here :. Saisissez cette ligne dans la variable pat. (en remplaçant le dernier ":" par un "/"). Et définit $ 6 sur rien pour éviter d'utiliser la ligne d'en-tête comme ligne de fichier valide.
    • si le champ $ 6 a un numéro valide, c'est une date. Comparez-le avec l'ancienne date oldd.
    • Est-ce plus vieux? Enregistrez ensuite les nouvelles valeurs pour old date oldd et old filename oldf. BTW, Oldf est non seulement 8ème terrain, mais du 8ème à la fin. C'est pourquoi une boucle à concaténer du 8 au NF (fin).
    • Compter les avances par un
    • FIN en imprimant le résultat

Le lancer:

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

Date la plus ancienne: 19691231

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

Total comparé: 111438

real 0m1.135s

utilisateur 0m0.872s

sys 0m0.760s


EDIT 2: Même concept, meilleure solution en utilisant find pour regarder le heure d'accès (utilisez %T avec la première printf pour heure de modification ou %C pour changement d'état à 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 heure de modification et affiche également la progression incrémentielle lorsqu’elle trouve des fichiers de plus en plus anciens, ce qui est utile lorsque vous avez des horodatages incorrects (comme le 19.01.1990):

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}'
5
Dr Beco

Veuillez utiliser ls - la page de manuel vous explique comment commander le répertoire.

ls -clt | head -n 2

Le -n 2 est tel que vous n'obtenez 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 (obtenir le fichier le plus récent)

ls -tr | head -n 1

Beaucoup plus facile que d’utiliser find, beaucoup plus rapide et plus robuste - n’ayez pas à vous soucier des formats de nommage des fichiers. Cela devrait également fonctionner sur presque tous les systèmes.

4
user1363990
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
2
Okki

Il semble que par "plus ancien", la plupart des gens ont supposé que vous entendiez par "temps de modification le plus ancien". C'est probablement corrigé, selon l'interprétation la plus stricte de "plus ancien", mais si vous vouliez celle avec le plus ancien accès heure, je modifierais la meilleure réponse ainsi:

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

Notez le %A+.

0
PenguinLust