Je cherche un shell one-liner pour 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.
Réponses:
Cela fonctionne (mis à jour pour intégrer la suggestion de Daniel Andersson):
find -type f -printf '%T+ %p\n' | sort | head -n 1
find
est vide car mon nom de fichier contient newline.
Celui-ci est un peu plus portable et, comme il ne repose pas sur l' find
extension GNU -printf
, il fonctionne donc aussi sous BSD / OS X:
find . -type f -print0 | xargs -0 ls -ltr | head -n 1
Le seul inconvénient ici est qu'il est quelque peu limité à la taille de ARG_MAX
(ce qui ne devrait pas être pertinent pour la plupart des nouveaux noyaux). Donc, 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 l'est pas.
Quelques solutions supplémentaires à ce problème sont décrites ci-dessous: Comment trouver le dernier fichier (le plus récent, le plus ancien, le plus ancien) dans un répertoire? - Wiki de Greg
xargs: ls: terminated by signal 13
erreur est également émise . Je suppose que c'est SIGPIPE. Je n'ai aucune idée pourquoi je ne reçois pas une erreur similaire lorsque je dirige la sortie du tri vers la solution.
head
commande qui se ferme une fois qu’elle a lu une ligne et "rompt" donc le tuyau, je pense. Vous n'obtenez pas l'erreur parce sort
qu'il ne semble pas s'en plaindre, mais ls
dans l'autre cas.
xargs
doivent être appelés ls
plus d'une fois. Dans ce cas, les sorties triées de ces invocations multiples sont concaténées lorsqu'elles doivent être fusionnées.
ls
et globe oculaire le plus ancien fichier, votre solution probablement va dépasser la limite de longueur de ligne de commande, ce qui ls
doit être invoqué à plusieurs reprises. Vous aurez la mauvaise réponse mais vous ne saurez jamais.
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 byte ( \0
) null 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 -z
commutateur fait en sorte que sort et grep interprètent uniquement les octets nuls en tant que caractères de fin de ligne. Comme il n'y a pas de commutateur de ce type pour la tête, nous utilisons à la grep -m 1
place (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, trie numériquement ( sort -n
) les secondes écoulées depuis l'époque Unix est un peu plus rapide. sed supprime les secondes 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 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 - Sort - Stat
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.
Edition 1: ces modifications permettent d'analyser les fichiers et les répertoires avec des espaces. Il est assez rapide pour le publier à 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:
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 find
pour regarder le temps d’accès (utiliser %T
avec le premier printf
pour le temps de modification ou %C
pour changer le statut ).
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 l’ 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 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
est mauvaise pour les scripts car sa sortie n'est pas destinée aux machines, le formatage de la sortie varie selon les implémentations. Comme vous l’avez déjà dit, find
c’est bon pour les scripts, mais il peut également être utile d’ajouter ces informations avant de parler de ls
solutions.
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
Bien 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.
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
sort -n
.
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 au cas où vous voudriez celui avec le plus long temps d' accès , je modifierais la meilleure réponse ainsi:
find -type f -printf '%A+ %p\n' | sort | head -n 1
Remarquez le %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'
imprime les dates et les noms de fichiers sur deux colonnes.sort | head -n1
conserve la ligne correspondant au fichier le plus ancien.echo $2
affiche la deuxième colonne, c'est-à-dire le nom du fichier.
find -type f -printf '%T+ %p\n' | sort | head -1