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


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


68
2018-02-15 16:06


origine




Réponses:


Cela fonctionne (mis à jour pour incorporer la suggestion de Daniel Andersson):

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

68
2018-02-15 16:13



Moins de frappe: find -type f -printf '%T+ %p\n' | sort | head -1 - Daniel Andersson
Je reçois un espace vide en raison de ma première ligne find est vide car le nom du fichier contient newline. - 林果皞
Puis-je demander si cela utilise la date de création ou de modification? - MrMesees
Linux ne stocke la date de création du fichier nulle part [*]. Ceci utilise la date de modification. [*] c'est en fait pas vrai; ext4 stocke la date de création de l'inode, mais il n'est pas exposé via des appels système et vous devez utiliser debugfs pour le voir.) - Marius Gedminas


Celui-ci est un peu plus portable et ne repose pas sur le GNU find extension -printf, donc ça marche aussi sur 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 (lequel devrait être sans importance pour la plupart des nouveaux noyaux). Donc, s'il y a plus de getconf ARG_MAX caractères retournés (262 144 sur mon système), cela ne vous donne pas le résultat correct. Ce n'est pas non plus compatible avec POSIX parce que -print0 et xargs -0 n'est pas

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


11
2018-02-15 16:19



Cela fonctionne aussi, mais il émet aussi un xargs: ls: terminated by signal 13 erreur comme effet secondaire. Je suppose que c'est SIGPIPE. Je n’ai aucune idée de la raison pour laquelle je n’obtiens pas une erreur similaire lorsque j’arrête de trier la sortie vers ma solution. - Marius Gedminas
Votre version est également plus facile à taper depuis la mémoire. :-) - Marius Gedminas
Oui, c'est un tuyau cassé. Je ne comprends pas cela avec les versions GNU et BSD de toutes ces commandes, mais c'est la headcommande qui se ferme une fois qu'il a lu une ligne et donc "casse" le tuyau, je pense. Vous ne recevez pas l'erreur parce que sort ne semble pas s'en plaindre, mais ls fait dans l'autre cas. - slhck
Cela casse si il y a tellement de noms de fichiers xargs doit invoquer ls plus d'une fois. Dans ce cas, les sorties triées de ces appels multiples se retrouvent concaténées lorsqu'elles doivent être fusionnées. - Nicole Hamilton
Je pense que c'est pire que de poster un script qui suppose que les noms de fichiers ne contiennent jamais d’espaces. La plupart du temps, ceux-ci fonctionneront car les noms de fichiers n'ont pas d'espaces. Et quand ils échouent, vous obtenez une erreur. Mais il est peu probable que cela fonctionne dans des cas réels et l'échec ne sera pas découvert. Sur n'importe quelle arborescence assez grande pour que vous ne puissiez pas simplement ls il et regarder le plus vieux fichier, votre solution probablement volonté dépasser la limite de longueur de la ligne de commande, provoquant ls être invoqué plusieurs fois. Vous aurez la mauvaise réponse mais vous ne saurez jamais. - Nicole Hamilton


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/[^ ]* //')"

Utiliser un octet nul (\0) au lieu d'un caractère de retour à la ligne (\n) s'assure que la sortie de find sera toujours compréhensible si l’un des noms de fichiers contient un caractère de retour à la ligne.

le -z switch fait à la fois trier et grep interpréter uniquement les octets nuls comme caractères de fin de ligne. Comme il n'y a pas un tel interrupteur pour la tête, nous utilisons grep -m 1 au lieu de cela (une seule occurrence).

Les commandes sont classées par temps d'exécution (mesurées 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. La tuyauterie à chat évite de colorer la sortie.

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

  • La dernière commande ne fait 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 associées: trouver - grep - sed - Trier - stat


11
2018-02-16 04:02





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

ls -clt | head -n 2

Le -n 2 est donc vous n'obtenez pas le "total" dans la sortie. Si vous ne voulez que 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 - ne vous inquiétez pas des formats de noms de fichiers. Il devrait également fonctionner sur presque tous les systèmes.


4
2017-08-07 03:13



Cela ne fonctionne que si les fichiers sont dans un seul répertoire, alors que ma question concernait une arborescence de répertoires. - Marius Gedminas


Bien que la réponse acceptée et les autres ici fassent le travail, si vous avez un très grand arbre, tous vont trier tous les fichiers.

Mieux serait si nous pouvions simplement les énumérer et garder la trace des plus anciens, sans avoir besoin de trier du tout.

Thats pourquoi je suis venu avec 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 pourrait être utile, même si la question est un peu ancienne.


Edit 1: Ces modifications permettent d'analyser des fichiers et des répertoires avec des espaces. C'est assez rapide pour le lancer à la racine /et trouver le plus vieux fichier jamais.

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" / * répertorie tous les fichiers (*), format long (l), récursivement (R), sans tri (U) pour être rapide, et le redirige vers awk
  • Awk puis BEGIN en mettant à zéro le compteur (optionnel à cette question) et en définissant la date la plus ancienne à être aujourd'hui, format YearMonthDay.
  • La boucle principale en premier
    • Récupère le 6ème champ, la date, le format Année-Mois-Jour et le remplace par YearMonthDay (si votre ls ne génère pas de cette manière, vous devrez peut-être le régler).
    • En utilisant récursif, il y aura des lignes d'en-tête pour tous les répertoires, sous la forme de / répertoire / ici :. Prenez cette ligne dans la variable pat. (en remplaçant le dernier ":" par un "/"). Et définit $ 6 à rien pour éviter d'utiliser la ligne d'en-tête comme une 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 que c'est plus vieux? Ensuite, enregistrez les nouvelles valeurs pour l’ancienne date 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 à concaténer du 8 au NF (fin).
    • Compter les avances par un
    • FIN en imprimant le résultat

En cours d'exécution:

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

Date la plus ancienne: 19691231

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

Total comparé: 111438

vrai 0m1.135s

utilisateur 0m0.872s

sys 0m0.760s


EDIT 2: Même concept, meilleure solution en utilisant find regarder le temps d'accès (utilisation %T avec le premier printf pour temps de modification ou %C pour changement de statut au lieu).

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émentielle comme il trouve des fichiers plus anciens et 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}'

4
2018-06-19 15:43



Il a encore besoin de tweeking pour accepter les fichiers avec des espaces. Je vais le faire bientôt. - Dr Beco
Je pense que l'analyse des fichiers contenant des espaces n'est pas une bonne idée. Peut-être en utilisant find. - Dr Beco
Il suffit de l'exécuter dans l'arborescence entière "/". Temps passé: Total comparé: 585744 réel 2m14.017s utilisateur 0m8.181s sys 0m8.473s - Dr Beco
En utilisant ls est mauvais pour les scripts car sa sortie n'est pas destinée aux machines, le formatage des sorties varie selon les implémentations. Comme vous l'avez déjà dit find est bon pour le script mais il pourrait aussi être bon d'ajouter cette information avant de parler de ls solutions. - Sampo Sarrala


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

2
2018-02-16 01:37



Cela ne fonctionnera pas correctement s'il y a des fichiers antérieurs au 9 septembre 2001 (1000000000 secondes depuis l'époque Unix). Pour activer le tri numérique, utilisez sort -n. - Dennis
Cela aide à me trouver le fichier, mais il est difficile de voir quel âge il a sans exécuter une deuxième commande :) - Marius Gedminas


Il semble que par "plus vieux" la plupart des gens ont supposé que vous vouliez dire "plus ancienne date de modification". Cela a probablement été corrigé, selon l'interprétation la plus stricte de "plus ancien", mais au cas où vous vouliez celui avec le plus ancien accès temps, je modifierais donc la meilleure réponse:

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

Notez le %A+.


0
2017-12-16 17:57