¿Cómo encontrar y enumerar recursivamente los últimos archivos modificados en un directorio con subdirectorios y tiempos?

417
  • Sistema operativo: Linux

  • Tipo de sistema de archivos: ext3

  • Solución preferida: bash (script / oneliner), ruby, python

Tengo varios directorios con varios subdirectorios y archivos en ellos. Necesito hacer una lista de todos estos directorios que esté construida de tal manera que cada directorio de primer nivel aparezca junto a la fecha y hora del último archivo creado / modificado dentro de él.

Para aclarar, si toco un archivo o modifico su contenido unos niveles de subdirectorio hacia abajo, esa marca de tiempo debe mostrarse junto al nombre del directorio de primer nivel. Digamos que tengo un directorio estructurado así:

./alfa/beta/gamma/example.txt

y modifico el contenido del archivo example.txt, necesito que ese tiempo se muestre al lado del directorio de primer nivel alfaen forma legible para humanos, no en época. He intentado algunas cosas usando Búsqueda xargs, sorty los gustos, pero no puedo evitar el problema de que la marca de tiempo del sistema de ficheros de 'alfa' no cambia cuando se crea / modifica los archivos unos niveles más abajo.

fredrik
fuente
Si puede soportar el dolor de construirlo, puede usar github.com/shadkam/recentmost .
user3392225
44
Increíble. 16 respuestas, y la mayoría / todos ni siquiera intentan hacer lo que el OP especificó ...
hmijail llora a los representantes el
En lugar de soluciones como un interruptor -R, solo veo el volumen aquí.
neverMind9
Debería ser una característica nativa.
neverMind9

Respuestas:

486

Prueba este:

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

Ejecútelo con la ruta al directorio donde debería comenzar a escanear de forma recursiva (admite nombres de archivos con espacios).

Si hay muchos archivos, puede pasar un tiempo antes de que devuelva algo. El rendimiento puede mejorarse si usamos en su xargslugar:

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

que es un poco más rápido

Heppo
fuente
132
Su "método rápido" también debería poder utilizar print0 para admitir espacios e incluso avances de línea en los nombres de archivo. find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head Esto es lo que uso: Esto todavía se las arregla para ser rápido para mí.
Dan
20
En Mac OS X no es la estadística de GNU, por lo que el comando falla. Tienes que brew install coreutilsusar y usar en gstatlugar destat
CharlesB
36
No necesita correr statya que find PATH -type f -printf "%T@ %p\n"| sort -nrhace el trabajo. También es un poco más rápido de esa manera.
nr
44
¿podemos de alguna manera convertir el comentario de @ user37078 en una respuesta real o editar la respuesta original? parece ser "la forma correcta" [tm] de hacerlo.
mnagel
66
En Mac OS X, sin instalar gstat ni nada más, puede hacer:find PATH -type f -exec stat -f "%m %N" "{}" \; | sort -nr | head
cobbzilla
198

Para encontrar todos los archivos, ese estado de archivo se modificó por última vez hace N minutos:

find -cmin -N

por ejemplo:

find -cmin -5

iman
fuente
44
+1 Gracias, muy útil. Funciona en Windows usando GnuWin32 find.
Sabuncu
muy conciso ¡muy agradable!
Randy L
es más rápido que otras soluciones más complicadas
david.perez
20
Muy bueno, también puede usar 'find -ctime -50' por ejemplo para los últimos 50 días de cambio.
Gorkem el
1
Para excluir el desorden, usesudo find -cmin -1 2>&1 |grep -v /proc/
Cees Timmerman el
39

GNU Find (ver man find) tiene un -printfparámetro para mostrar los archivos EPOC mtime y el nombre de ruta relativo.

redhat> find . -type f -printf '%T@ %P\n' | sort -n | awk '{print $2}'
usuario2570243
fuente
3
¡Gracias! Esta es la única respuesta que es lo suficientemente rápida como para buscar a través de mi amplia estructura de directorios en un tiempo razonable. Paso la salida tailpara evitar que se impriman miles de líneas en la salida.
sffc
8
Otro comentario: la awk '{print $2}'parte parece causar problemas cuando hay nombres de archivo con espacios. Aquí hay una solución que usa sed, y también imprime el tiempo además de la ruta:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
sffc
3
Creo que debería ser ordenado -rn
Bojan Dević
2
La variante -printf es mucho más rápida que llamar a un proceso 'stat' cada vez; corta horas de mi (s) trabajo (s) de respaldo. Gracias por informarme de esto. Evité lo awk / sed ya que solo me preocupa la última actualización dentro del árbol, así que X = $ (find / path -type f -printf '% T% p \ n' | grep -v something-I- don-tcare-about | sort -nr | head -n 1) y un echo $ {X # * ""} me funcionaron bien (dame cosas hasta el primer espacio)
David Goodwin
2
Todo no funcionará si el nombre de archivo en varias líneas. Use touch "lala<Enter>b"para crear dicho archivo. Creo que el diseño de utilidades de Unix tiene una gran falla sobre el nombre de archivo.
Fruta
35

Acorté la increíble respuesta de Halo a esta frase.

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

Actualizado : si hay espacios en los nombres de archivo, puede usar esta modificación

OFS="$IFS";IFS=$'\n';stat --printf="%y %n\n" $(ls -tr $(find . -type f));IFS="$OFS";
slashdottir
fuente
¿Qué tal esto: IFS = $ '\ n'; stat --printf = "% y% n \ n" $ (ls -tr $ (find. -type f))
slashdottir
3
Esto no funcionará si tiene una gran cantidad de archivos. Las respuestas que usan xargs resuelven ese límite.
carl verbiest
@carlverbiest de hecho, una gran cantidad de archivos romperán la solución de slashdottir. Incluso las soluciones basadas en xargs serán lentas entonces. La solución de user2570243 es la mejor para grandes sistemas de archivos.
Stéphane Gourichon
IFS=$'\n'no es seguro en ningún caso cuando se manejan nombres de archivos: las nuevas líneas son caracteres válidos en los nombres de archivos en UNIX. Solo se garantiza que el carácter NUL no estará presente en una ruta.
Charles Duffy
17

Prueba esto

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

Se utiliza findpara recopilar todos los archivos del directorio, lspara enumerarlos ordenados por fecha de modificación, headpara seleccionar el primer archivo y finalmente statpara mostrar la hora en un formato agradable.

En este momento no es seguro para archivos con espacios en blanco u otros caracteres especiales en sus nombres. Escriba una recomendación si aún no satisface sus necesidades.

Daniel Böhmer
fuente
1
halo: me gusta tu respuesta, funciona bien e imprime el archivo correcto. Sin embargo, no me ayuda, ya que hay demasiados subniveles en mi caso. Entonces obtengo "Lista de argumentos demasiado larga" para ls ... y xargs tampoco ayudaría en este caso. Intentaré algo más.
fredrik
En ese caso, es un poco más complejo y necesitará algún programa real. Hackearé un poco de Perl.
Daniel Böhmer
1
Resolví esto usando PHP en su lugar. Una función recursiva que desciende a través del árbol del sistema de archivos y almacena la hora del archivo modificado más recientemente.
fredrik
11

Este comando funciona en Mac OS X:

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

En Linux, como preguntaba el cartel original, use en statlugar degstat .

Esta respuesta es, por supuesto, la solución sobresaliente de user37078 , promovida de comentario a respuesta completa. He mezclado en charlesb visión 's para uso gstaten Mac OS X. Tengo coreutils de MacPorts en lugar de homebrew , por cierto.

Y así es como empaqueté esto en un comando simple ~/bin/ls-recent.shpara reutilizar:

#!/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
Jim DeLaHunt
fuente
2
En OS X yosemite; Me sale el error: find: ftsopen: No existe tal archivo o directorio
Reece
Interesante. ¿Qué comando escribiste (con parámetros)? ¿Y cuáles eran los nombres de los archivos en ese directorio? Y si creó su propia versión de ~/bin/ls-recent.sh, ¿ha revisado cuidadosamente el script en busca de diferencias?
Jim DeLaHunt
10
Para aquellos que no quieren instalar nada en Mac OS X:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Jake
5

Las soluciones perl y Python en esta publicación me ayudaron a resolver este problema en Mac OS X: /unix/9247/how-to-list-files-sorted-by-modification-date-recursively -no-stat-command-avail .

Citando de la publicación:

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
    }'

Pitón:

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'
William Niu
fuente
5

Ignorar archivos ocultos - con sello de tiempo agradable y rápido

Maneja bien los espacios en los nombres de archivo, ¡no es que debas usarlos!

$ 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

Sefind puede encontrar más en abundancia siguiendo el enlace.

Serge Stroobandt
fuente
3

Estoy mostrando esto para el último tiempo de acceso, puede modificarlo fácilmente para hacer el último tiempo de modificación.

Hay dos formas de hacer esto:


1) Si desea evitar la ordenación global, que puede ser costosa si tiene decenas de millones de archivos, puede hacer lo siguiente: (ubicarse en la raíz del directorio donde desea que comience la búsqueda)

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 

El método anterior imprime los nombres de archivo con un tiempo de acceso progresivamente más nuevo y el último archivo que imprime es el archivo con el último tiempo de acceso. Obviamente, puede obtener el último tiempo de acceso utilizando una "cola -1".


2) Puede buscar imprimir el nombre de forma recursiva, el tiempo de acceso de todos los archivos en su subdirectorio y luego ordenar en función del tiempo de acceso y la cola de la entrada más grande:

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

Y ahí lo tienes...

Sean
fuente
3

Tengo este alias en mi perfil que uso con bastante frecuencia.

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

Por lo tanto, hace lo que está buscando (con la excepción de que no atraviesa el cambio de fecha / hora en varios niveles): busca los archivos más recientes (archivos * .log y * .trc en este caso); también solo encuentra archivos modificados en el último día, luego los ordena por tiempo y canaliza la salida a través de menos:

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

PD. Tenga en cuenta que no tengo root en algunos de los servidores, pero siempre tengo sudo, por lo que es posible que no necesite esa parte.

Tagar
fuente
¿Cómo es esto "exactamente lo que estás buscando"? El OP escribió una buena explicación de lo que quería, y esto lo ignora por completo.
hmijail llora a los representantes el
gracias por señalar eso estás en lo correcto: este método no va a varios niveles para cambiar la fecha / hora, solo muestra la fecha / hora de los archivos de directorios dentro de él. edité mi respuesta.
Tagar
1

Puede dar el comando printf de buscar un intento

El último tiempo de acceso de% Ak File en el formato especificado por k, que es @' or a directive for the C la función strftime '. Los valores posibles para k se enumeran a continuación; Es posible que algunos de ellos no estén disponibles en todos los sistemas, debido a las diferencias en 'strftime' entre sistemas.

graugans
fuente
1

Función de golpe rápido:

# 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"
}

Encuentre el último archivo modificado en un directorio:

findLatestModifiedFiles "/home/jason/" 1

También puede especificar su propio formato de fecha / hora como tercer argumento.

Jason Larke
fuente
1

Lo siguiente le devuelve una cadena de la marca de tiempo y el nombre del archivo con la marca de tiempo más reciente:

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

Resultando a una salida de la forma: <yy-mm-dd-hh-mm-ss.nanosec> <filename>

mark_infinite
fuente
1

Aquí hay una versión que funciona con nombres de archivo que pueden contener espacios, líneas nuevas y caracteres globales también:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printf imprime la modificación del archivo (valor EPOCH) seguido de un espacio y \0 nombres de archivo terminados.
  • sort -zk1nr lee datos terminados en NUL y los ordena inversamente numéricamente

Como la pregunta está etiquetada con Linux, así que supongo gnu hay utilidades disponibles.

Puede canalizar arriba con:

xargs -0 printf "%s\n"

para imprimir la hora de modificación y los nombres de archivos ordenados por hora de modificación (el más reciente primero) terminados por nuevas líneas

anubhava
fuente
1

Esto es lo que estoy usando (muy eficiente):

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

PROS:

  • solo genera 3 procesos

USO:

find_last [dir [number]]

dónde:

  • dir - un directorio para buscar [directorio actual]
  • number - número de archivos más nuevos para mostrar [10]

La salida para se find_last /etc 4ve así:

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
Seweryn Niemiec
fuente
0

Para una lssalida simple , use esto. No hay una lista de argumentos, por lo que no puede ser demasiado larga:

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

Y se justifica con cutsolo las fechas, horas y nombre:

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

EDITAR : Acabo de notar que la respuesta principal actual se ordena por fecha de modificación. Eso es igual de fácil con el segundo ejemplo aquí, ya que la fecha de modificación es la primera en cada línea: coloque una clasificación al final:

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

Esto también se puede hacer con una función recursiva en bash

Sea F una función que muestre la hora del archivo que debe poder clasificarse lexicográficamente aaaa-mm-dd, etc. (¿dependiente del sistema operativo?)

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

R la función recursiva que se ejecuta a través de directorios

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

Y finalmente

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