Comment trouver récursivement le dernier fichier modifié dans un répertoire?

Réponses:

357
find . -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "

Pour un arbre énorme, il peut être difficile de sorttout garder en mémoire.

%T@vous donne l'heure de modification comme un horodatage Unix, sort -ntrie numériquement, tail -1prend la dernière ligne (horodatage le plus élevé), cut -f2 -d" "coupe le premier champ (l'horodatage) de la sortie.

Edit: Tout comme -printfprobablement uniquement GNU, l'utilisation correcte de l' stat -cest également. Bien qu'il soit possible de faire de même sur BSD, les options de formatage sont différentes ( -f "%m %N"il semblerait)

Et j'ai raté la partie du pluriel; si vous voulez plus que le dernier fichier, augmentez simplement l'argument tail.

plundra
la source
7
Si l'ordre est important, vous pouvez changer d'utilisation sort -rn | head -3au lieu de sort -n | tail -3. Une version donne les fichiers du plus ancien au plus récent, tandis que l'autre va du plus récent au plus ancien.
Don Faulkner
3
J'avais un énorme répertoire (une dizaine de milliers de petits fichiers) et j'étais inquiet pour les performances, mais ... cette commande s'exécutait en moins d'une seconde! Merci beaucoup !!! :-)
lucaferrario
2
"Pour un arbre immense, il pourrait être difficile pour le tri de tout garder en mémoire." sortva créer des fichiers temporaires (in /tmp) au besoin, donc je ne pense pas que ce soit un problème.
Vladimir Panteleev
1
en le modifiant: -printf '%T@ %Tb %Td %TY %p\n'vous donnera un horodatage (si nécessaire) (similaire à ls)
bshea
1
Je trouve ce qui suit plus court et avec une sortie plus interprétable:find . -type f -printf '%TF %TT %p\n' | sort | tail -1
snth
129

Suite à la réponse de @ plundra , voici la version BSD et OS X:

find . -type f -print0 | xargs -0 stat -f "%m %N" |
sort -rn | head -1 | cut -f2- -d" "
Emerson Farrugia
la source
1
Est-ce que BSD / OS X prend en findcharge +au lieu de \;? Parce que cela fait la même chose (passer plusieurs fichiers en tant que paramètres), sans le -print0 | xargs -0tube.
DevSolar
Que faire si je souhaite modifier les 5 ou n derniers fichiers en ordre décroissant?
khunshan
@khunshan changer head -1àhead -5
Emerson Farrugia
20

Au lieu de trier les résultats et de ne conserver que les derniers modifiés, vous pouvez utiliser awk pour imprimer uniquement celui avec le plus grand temps de modification (en temps unix):

find . -type f -printf "%T@\0%p\0" | awk '
    {
        if ($0>max) {
            max=$0; 
            getline mostrecent
        } else 
            getline
    } 
    END{print mostrecent}' RS='\0'

Cela devrait être un moyen plus rapide de résoudre votre problème si le nombre de fichiers est suffisamment important.

J'ai utilisé le caractère NUL (c'est-à-dire '\ 0') parce que, théoriquement, un nom de fichier peut contenir n'importe quel caractère (y compris l'espace et la nouvelle ligne) mais cela.

Si vous n'avez pas de tels noms de fichiers pathologiques dans votre système, vous pouvez également utiliser le caractère de nouvelle ligne:

find . -type f -printf "%T@\n%p\n" | awk '
    {
        if ($0>max) {
            max=$0; 
            getline mostrecent
        } else 
            getline
    } 
    END{print mostrecent}' RS='\n'

De plus, cela fonctionne aussi dans mawk.

marco
la source
Cela pourrait être facilement adapté pour garder les trois plus récents.
pause jusqu'à nouvel ordre.
1
Cela ne fonctionne pas avec mawkl'alternative standard Debian.
Jan
Non, mais dans ce cas, vous pouvez utiliser le caractère de nouvelle ligne s'il ne vous dérange pas;)
marco
10

J'ai eu du mal à trouver le dernier fichier modifié sous Solaris 10. Il findn'y a pas d' printfoption et statn'est pas disponible. J'ai découvert la solution suivante qui fonctionne bien pour moi:

find . -type f | sed 's/.*/"&"/' | xargs ls -E | awk '{ print $6," ",$7 }' | sort | tail -1

Pour afficher également le nom de fichier

find . -type f | sed 's/.*/"&"/' | xargs ls -E | awk '{ print $6," ",$7," ",$9 }' | sort | tail -1

Explication

  • find . -type f recherche et répertorie tous les fichiers
  • sed 's/.*/"&"/' encapsule le chemin entre guillemets pour gérer les espaces blancs
  • xargs ls -Eenvoie le chemin entre guillemets à ls, l' -Eoption s'assure qu'un horodatage complet (format année-mois-jour heure-minute-secondes-nanosecondes ) est retourné
  • awk '{ print $6," ",$7 }' extrait uniquement la date et l'heure
  • awk '{ print $6," ",$7," ",$9 }' extrait la date, l'heure et le nom du fichier
  • sort renvoie les fichiers triés par date
  • tail -1 renvoie uniquement le dernier fichier modifié
Florian Feldhaus
la source
9

Cela semble fonctionner correctement, même avec des sous-répertoires:

find . -type f | xargs ls -ltr | tail -n 1

En cas de trop de fichiers, affinez la recherche.

mgratia
la source
1
L' -loption lssemble inutile. -trSemble juste suffisant.
Acumenus
6
Cela semble trier par répertoire, donc il n'affichera pas nécessairement le dernier fichier
Fabian Schmengler
1
Dans le cas où il y a des espaces dans les chemins de fichiers, mieux faire:find . -type f -print0 | xargs -0 ls -ltr | tail -n 1
Maintenance périodique
Cela ne trouve pas les derniers fichiers pour moi.
K.-Michael Aye
1
Pensez que cela se casse s'il y a des espaces dans les noms de fichiers
waferthin
7

Affiche le dernier fichier avec un horodatage lisible par l'homme:

find . -type f -printf '%TY-%Tm-%Td %TH:%TM: %Tz %p\n'| sort -n | tail -n1

Le résultat ressemble à ceci:

2015-10-06 11:30: +0200 ./foo/bar.txt

Pour afficher plus de fichiers, remplacez-les -n1par un nombre plus élevé

Fabian Schmengler
la source
5

J'utilise quelque chose de similaire tout le temps, ainsi que la liste top-k des fichiers les plus récemment modifiés. Pour les grandes arborescences de répertoires, il peut être beaucoup plus rapide d'éviter le tri . Dans le cas du premier fichier modifié en dernier lieu:

find . -type f -printf '%T@ %p\n' | perl -ne '@a=split(/\s+/, $_, 2); ($t,$f)=@a if $a[0]>$t; print $f if eof()'

Sur un répertoire contenant 1,7 million de fichiers, j'obtiens le plus récent en 3,4s, une accélération de 7,5x contre la solution de 25,5s en utilisant sort.

Pierre D
la source
Très cool: je viens d'échanger la dernière copie avec: system ("ls -l $ f") if eof () pour voir la date de manière sympa aussi.
Martin T.
@MartinT. : super, et vous êtes les bienvenus. C'est étrange pour moi que les gens aient cet instinct pour trier les choses (O (n log n)) lorsqu'une méthode O (n) est disponible. Cela semble être la seule réponse évitant le tri. BTW, le but de la commande que j'ai suggérée est juste de trouver le chemin du dernier fichier. Vous pouvez alias la commande dans votre shell (par exemple as lastfile) et vous pouvez ensuite faire ce que vous voulez avec le résultat, comme ls -l $(lastfile .), ou open $(lastfile .)(sur un Mac), etc.
Pierre D
Oh, je me corrige: je vois une autre réponse ci-dessous (@marco). +1.
Pierre D
4

Cela donne une liste triée:

find . -type f -ls 2>/dev/null | sort -M -k8,10 | head -n5

Inversez l'ordre en plaçant un «-r» dans la commande de tri. Si vous ne voulez que des noms de fichiers, insérez "awk '{print $ 11}' |" avant '| tête'

Karlo
la source
3

Sur Ubuntu 13, ce qui suit le fait, peut-être un peu plus vite, car il inverse le tri et utilise 'head' au lieu de 'tail', ce qui réduit le travail. Pour afficher les 11 fichiers les plus récents dans une arborescence:

trouver . -type f -printf '% T @% p \ n' | sort -n -r | tête -11 | cut -f2- -d "" | sed -e, ^. / ,, '| xargs ls -U -l

Cela donne une liste complète de ls sans re-tri et omet l'ennuyeux './' que 'find' met sur chaque nom de fichier.

Ou, en tant que fonction bash:

treecent () {
  local numl
  if [[ 0 -eq $# ]] ; then
    numl=11   # Or whatever default you want.
  else
    numl=$1
  fi
  find . -type f -printf '%T@ %p\n' | sort -n -r | head -${numl} |  cut -f2- -d" " | sed -e 's,^\./,,' | xargs ls -U -l
}

Pourtant, la plupart du travail a été fait par la solution originale de plundra. Merci plundra.

RickyS
la source
3

J'ai fait face au même problème. J'ai besoin de trouver le fichier le plus récent de manière récursive. trouver a pris environ 50 minutes pour trouver.

Voici un petit script pour le faire plus rapidement:

#!/bin/sh

CURRENT_DIR='.'

zob () {
    FILE=$(ls -Art1 ${CURRENT_DIR} | tail -n 1)
    if [ ! -f ${FILE} ]; then
        CURRENT_DIR="${CURRENT_DIR}/${FILE}"
        zob
    fi
    echo $FILE
    exit
}
zob

Il s'agit d'une fonction récursive qui obtient l'élément modifié le plus récent d'un répertoire. Si cet élément est un répertoire, la fonction est appelée récursivement et recherche dans ce répertoire, etc.

AnatomicJC
la source
3

Je trouve ce qui suit plus court et avec une sortie plus interprétable:

find . -type f -printf '%TF %TT %p\n' | sort | tail -1

Compte tenu de la longueur fixe des heures de format standard ISO, le tri lexicographique est très bien et nous n'avons pas besoin de l' -noption sur le tri.

Si vous souhaitez supprimer à nouveau les horodatages, vous pouvez utiliser:

find . -type f -printf '%TFT%TT %p\n' | sort | tail -1 | cut -f2- -d' '
snth
la source
2

Si l'exécution statsur chaque fichier individuellement est trop lente, vous pouvez utiliser xargspour accélérer un peu les choses:

find . -type f -print0 | xargs -0 stat -f "%m %N" | sort -n | tail -1 | cut -f2- -d" " 
Mattias Wadman
la source
2

Cela modifie récursivement l'heure de modification de tous les répertoires du répertoire actuel par le fichier le plus récent de chaque répertoire:

for dir in */; do find $dir -type f -printf '%T@ "%p"\n' | sort -n | tail -1 | cut -f2- -d" " | xargs -I {} touch -r {} $dir; done
rwhirn
la source
1
Il casse mal si des répertoires contiennent des espaces - besoin de définir IFS et d'utiliser des guillemets: IFS = $ '\ n'; pour dir dans $ (find ./ -type d); faire écho "$ dir"; trouver "$ dir" -type f -printf '% T @ "% p" \ n' | sort -n | queue -1 | cut -f2- -d "" | xargs -I {} touch -r {} "$ dir"; terminé;
Andy Lee Robinson
2

Ce simple cli fonctionnera également:

ls -1t | head -1

Vous pouvez changer le -1 au nombre de fichiers que vous souhaitez lister

Ankit Zalani
la source
10
Non, ce ne sera pas le cas, car ce n'est pas récursif.
arataj
1

J'ai trouvé la commande ci-dessus utile, mais pour mon cas, j'avais besoin de voir la date et l'heure du fichier et j'ai eu un problème avec plusieurs fichiers qui ont des espaces dans les noms. Voici ma solution de travail.

find . -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" " | sed 's/.*/"&"/' | xargs ls -l
Roger Cable
la source
1

La commande suivante a fonctionné sur Solaris:

find . -name "*zip" -type f | xargs ls -ltr | tail -1 
RahulM
la source
1

Ignorer les fichiers cachés - avec un horodatage agréable et rapide

$ find . -type f -not -path '*/\.*' -printf '%TY.%Tm.%Td %THh%TM %Ta %p\n' |sort -nr |head -n 10

Résultat

Gère bien les espaces dans les noms de fichiers - pas que ceux-ci doivent être utilisés!

2017.01.25 18h23 Wed ./indenting/Shifting blocks visually.mht
2016.12.11 12h33 Sun ./tabs/Converting tabs to spaces.mht
2016.12.02 01h46 Fri ./advocacy/2016.Vim or Emacs - Which text editor do you prefer?.mht
2016.11.09 17h05 Wed ./Word count - Vim Tips Wiki.mht

Plus

Plus à find gogo en suivant le lien.

Serge Stroobandt
la source
1

Pour rechercher des fichiers dans / target_directory et tous ses sous-répertoires, qui ont été modifiés au cours des 60 dernières minutes:

$ find /target_directory -type f -mmin -60

Pour rechercher les fichiers les plus récemment modifiés, triés dans l'ordre inverse de l'heure de mise à jour (c'est-à-dire les fichiers les plus récemment mis à jour en premier):

$ find /etc -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort -r
akash
la source
0

Je préfère celui-ci, il est plus court:

find . -type f -print0|xargs -0 ls -drt|tail -n 1
user3295940
la source
0

J'ai écrit un package pypi / github pour cette question car j'avais également besoin d'une solution.

https://github.com/bucknerns/logtail

Installer:

pip install logtail

Utilisation: queues fichiers modifiés

logtail <log dir> [<glob match: default=*.log>]

Usage2: Ouvre le dernier fichier modifié dans l'éditeur

editlatest <log dir> [<glob match: default=*.log>]
Nathan Buckner
la source