web-dev-qa-db-fra.com

Comment rechercher et répertorier de manière récursive les derniers fichiers modifiés dans un répertoire avec sous-répertoires et heures?

  • Système d'exploitation: Linux

  • Type de système de fichiers: ext3

  • Solution préférée: bash (script/oneliner), Ruby, python

J'ai plusieurs répertoires avec plusieurs sous-répertoires et fichiers. Je dois faire une liste de tous ces répertoires construite de manière à ce que chaque répertoire de premier niveau soit répertorié à côté de la date et de l'heure du dernier fichier créé/modifié qu'il contient.

Pour clarifier, si je touche un fichier ou modifie son contenu quelques niveaux plus bas, l'horodatage doit être affiché à côté du nom du répertoire de premier niveau. Disons que j'ai un répertoire structuré comme ceci:

./alfa/beta/gamma/example.txt

et je modifie le contenu du fichier example.txt, il me faut cette heure affichée à côté du répertoire de premier niveau alfa sous une forme lisible par l'homme, et non pas Epoch. J'ai essayé certaines choses en utilisant find, xargs, sort et les goûts, mais je ne parviens pas à résoudre le problème selon lequel l'horodatage du système de fichiers de 'alfa' ne change pas lorsque je crée/modifie des fichiers quelques niveaux plus bas.

377
fredrik

Essaye celui-là:

#!/bin/bash
find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head

Exécutez-le avec le chemin du répertoire où il doit commencer à analyser de manière récursive (il prend en charge les noms de fichiers avec des espaces).

S'il y a beaucoup de fichiers, cela peut prendre un certain temps avant que rien ne soit renvoyé. Les performances peuvent être améliorées si nous utilisons plutôt xargs:

#!/bin/bash
find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head

ce qui est un peu plus rapide.

441
Heppo

Pour trouver tous les fichiers dont le statut a été modifié pour la dernière fois N il y a quelques minutes:

find -cmin -N

par exemple:

find -cmin -5

176
iman

La recherche GNU (voir man find) a un paramètre -printf permettant de déplacer les fichiers EPOC mtime et le nom de chemin relatif.

redhat> find . -type f -printf '%T@ %P\n' | sort -n | awk '{print $2}'
38
user2570243

J'ai raccourci la réponse impressionnante de halo à ce one-liner

stat --printf="%y %n\n" $(ls -tr $(find * -type f))

Mise à jour: S'il y a des espaces dans les noms de fichiers, vous pouvez utiliser cette modification

OFS="$IFS";IFS=$'\n';stat --printf="%y %n\n" $(ls -tr $(find . -type f));IFS="$OFS";
34
slashdottir

Essaye ça

#!/bin/bash
stat --format %y $(ls -t $(find alfa/ -type f) | head -n 1)

Il utilise find pour rassembler tous les fichiers du répertoire, ls pour les répertorier par date de modification, head pour sélectionner le 1er fichier et enfin stat pour afficher l'heure. un beau format.

Pour le moment, les noms de fichiers comportant des espaces ou d'autres caractères spéciaux ne sont pas sûrs. Rédigez une recommandation si elle ne répond pas encore à vos besoins.

15
Daniel Böhmer

Cette commande fonctionne sur Mac OS X:

find "$1" -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head

Sous Linux, comme l'a demandé l'affiche originale, utilisez stat au lieu de gstat.

Cette réponse est, bien sûr, la solution exceptionnelle de ser37078 , promue du commentaire à la réponse complète. J'ai mélangé à la perspicacité de CharlesB pour utiliser gstat sur Mac OS X. J'ai obtenu de coreutils de - MacPorts plutôt que homebrew , au fait.

Et voici comment j'ai intégré cela dans une simple commande ~/bin/ls-recent.sh pour le réutiliser:

#!/bin/bash
# ls-recent: list files in a dir tree, most recently modified first
#
# Usage: ls-recent path [-10 | more]
# 
# Where "path" is a path to target directory, "-10" is any arg to pass
# to "head" to limit the number of entries, and "more" is a special arg
# in place of "-10" which calls the pager "more" instead of "head".
if [ "more" = "$2" ]; then
   H=more; N=''
else
   H=head; N=$2
fi

find "$1" -type f -print0 |xargs -0 gstat --format '%Y :%y %n' \
    |sort -nr |cut -d: -f2- |$H $N
10
Jim DeLaHunt

Les solutions Perl et Python de ce message m'ont aidé à résoudre ce problème sous Mac OS X: https://unix.stackexchange.com/questions/9247/how-to-list-files -sorted-by-modification-date-récursivement-no-stat-commande-avail .

Citation de l'article:

Perl:

find . -type f -print |
Perl -l -ne '
    $_{$_} = -M;  # store file age (mtime - now)
    END {
        $,="\n";
        print sort {$_{$b} <=> $_{$a}} keys %_;  # print by decreasing age
    }'

Python:

find . -type f -print |
python -c 'import os, sys; times = {}
for f in sys.stdin.readlines(): f = f[0:-1]; times[f] = os.stat(f).st_mtime
for f in sorted(times.iterkeys(), key=lambda f:times[f]): print f'
5
William Niu

Je montre ceci pour la dernière heure d'accès, vous pouvez facilement le modifier pour faire les dernières modifications.

Il y a deux façons de faire cela:


1) Si vous voulez éviter le tri global qui peut coûter cher si vous avez des dizaines de millions de fichiers, alors vous pouvez faire: (positionnez-vous à la racine du répertoire où vous voulez que votre recherche commence)

linux> touch -d @0 /tmp/a;
linux> find . -type f -exec tcsh -f -c test `stat --printf="%X" {}` -gt  `stat --printf="%X" /tmp/a`  ; -exec tcsh -f -c touch -a -r {} /tmp/a ; -print 

La méthode ci-dessus imprime les noms de fichiers avec le temps d'accès le plus récent et le dernier fichier imprimé est le fichier avec le dernier temps d'accès. Vous pouvez évidemment obtenir la dernière heure d'accès en utilisant un "tail -1".


2) Vous pouvez faire en sorte d’imprimer récursivement le nom, l’heure d’accès de tous les fichiers de votre sous-répertoire, puis trier en fonction de l’heure d’accès et de la fin de la plus grande entrée:

linux> \find . -type f -exec stat --printf="%X  %n\n" {} \; | \sort -n | tail -1

Et voila...

3
Sean

Ignorer les fichiers cachés - avec horodatage Nice et rapide

Gère bien les espaces dans les noms de fichiers - vous ne devez pas les utiliser!

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

2017.01.28 07h00 Sat ./recent
2017.01.21 10h49 Sat ./hgb
2017.01.16 07h44 Mon ./swx
2017.01.10 18h24 Tue ./update-stations
2017.01.09 10h38 Mon ./stations.json

Plus find Galore peut être trouvé en suivant le lien.

3
Serge Stroobandt

J'ai cet alias dans mon .profile que j'utilise assez souvent

$ alias | grep xlogs
xlogs='Sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | Sudo xargs ls -ltr --color | less -R'

Donc, il fait ce que vous recherchez (sauf qu'il ne traverse pas plusieurs niveaux de date/heure de modification) - recherche les fichiers les plus récents (fichiers * .log et * .trc dans ce cas); de plus, il ne trouve que les fichiers modifiés le dernier jour, puis trie par heure et passe en sortie avec moins:

Sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | Sudo xargs ls -ltr --color | less -R

ps. Remarquez que je n'ai pas de racine sur certains serveurs, mais que j'ai toujours Sudo, vous n'aurez peut-être pas besoin de cette partie.

3
Tagar

Voici une version qui fonctionne avec les noms de fichiers pouvant contenir des espaces, des nouvelles lignes et des caractères globaux:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printf affiche la modification du fichier (valeur d'époque) suivie d'un espace et de _ noms de fichiers terminés \0.
  • sort -zk1nr lit les données terminées par NUL et les trie en sens inverse

Comme la question est étiquetée avec Linux, je suppose donc que gnu utils sont disponibles.

Vous pouvez diriger dessus avec:

xargs -0 printf "%s\n"

pour imprimer l’heure de modification et les noms de fichiers triés par heure de modification (le plus récent en premier) et terminés par une nouvelle ligne.

1
anubhava

Fonction de bash rapide:

# findLatestModifiedFiles(directory, [max=10, [format="%Td %Tb %TY, %TT"]])
function findLatestModifiedFiles() {
    local d="${1:-.}"
    local m="${2:-10}"
    local f="${3:-%Td %Tb %TY, %TT}"

    find "$d" -type f -printf "%T@ :$f %p\n" | sort -nr | cut -d: -f2- | head -n"$m"
}

Recherchez le dernier fichier modifié dans un répertoire:

findLatestModifiedFiles "/home/jason/" 1

Vous pouvez également spécifier votre propre format de date/heure comme troisième argument.

1
Jason Larke

Ce qui suit vous renvoie une chaîne d'horodatage et le nom du fichier avec l'horodatage le plus récent:

find $Directory -type f -printf "%TY-%Tm-%Td-%TH-%TM-%TS %p\n" | sed -r 's/([[:digit:]]{2})\.([[:digit:]]{2,})/\1-\2/' |     sort --field-separator='-' -nrk1 -nrk2 -nrk3 -nrk4 -nrk5 -nrk6 -nrk7 | head -n 1

Résultat en une sortie de la forme: <yy-mm-dd-hh-mm-ss.nanosec> <filename>

1
mark_infinite

Vous pouvez donner à la commande printf de trouver un essai

% Dernier accès du fichier Ak au format spécifié par k, qui est soit la fonction @' or a directive for the C strftime '. Les valeurs possibles pour k sont énumérées ci-dessous; certains d'entre eux peuvent ne pas être disponibles sur tous les systèmes, en raison des différences de "strftime" entre les systèmes.

0
graugans

C'est ce que j'utilise (très efficace):

function find_last () { find "${1:-.}" -type f -printf '%TY-%Tm-%Td %TH:%TM %P\n' 2>/dev/null | sort | tail -n "${2:-10}" }

AVANTAGES:

  • il ne génère que 3 processus

USAGE:

find_last [dir [number]]

où:

  • dir - un répertoire à rechercher [répertoire actuel]
  • number - nombre de fichiers les plus récents à afficher [10]

La sortie pour find_last /etc 4 ressemble à ceci:

2019-07-09 12:12 cups/printers.conf
2019-07-09 14:20 salt/minion.d/_schedule.conf
2019-07-09 14:31 network/interfaces
2019-07-09 14:41 environment
0
Seweryn Niemiec

Cela pourrait être fait avec une fonction reccursive dans bash aussi

Soit F une fonction qui affiche l’heure du fichier qui doit pouvoir être triée lexicographiquement aaaa-mm-jj etc., (dépendante de l’os?)

F(){ stat --format %y "$1";}                # Linux
F(){ ls -E "$1"|awk '{print$6" "$7}';}      # SunOS: maybe this could be done easier

R la fonction récursive qui traverse les répertoires

R(){ local f;for f in "$1"/*;do [ -d "$f" ]&&R $f||F "$f";done;}

Et enfin

for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done
0
Nahuel Fouilleul

Utilisez ceci pour la sortie simple ls. Il n'y a pas de liste d'arguments, donc ça ne peut pas être trop long:

find . | while read FILE;do ls -d -l "$FILE";done

Et sympa avec cut pour seulement les dates, les heures et le nom:

find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5

EDIT: Je viens de remarquer que la réponse la plus récente est classée par date de modification. C'est tout aussi facile avec le deuxième exemple ici, puisque la date de modification est la première sur chaque ligne - claquez un tri sur la fin:

find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5 | sort
0
Izkata