Encuentra el último archivo por fecha de modificación

39

Si quiero encontrar el último archivo (mtime) en un directorio (grande) que contenga subdirectorios, ¿cómo lo haría?

Muchas publicaciones que he encontrado sugieren alguna variación de ls -lt | head(de manera divertida, muchas sugieren ls -ltr | tailcuál es el mismo pero menos eficiente) lo cual está bien a menos que tenga subdirectorios (yo sí).

Por otra parte, podrías

find . -type f -exec ls -lt \{\} \+ | head

lo que definitivamente hará el truco para tantos archivos como pueda especificar un comando, es decir, si tiene un directorio grande , -exec...\+emitirá comandos separados; por lo tanto, cada grupo se ordenará por lssí mismo pero no por encima del conjunto total; Por lo tanto, la cabeza recogerá la última entrada del primer lote.

Alguna respuesta?

Rico
fuente
por cierto, no necesita ninguna de esas barras invertidas.
enzotib
@enzotib: lo haces ( \ + ), de lo contrario obtienesfind: missing argument to '-exec'
organiza el
@arrange: no tengo este error, ya que +no tiene ningún significado bash, por lo que no es necesario escapar.
enzotib
@enzotib: tienes razón, mi error, lo siento
organice el

Respuestas:

46

No necesita recurrir a comandos externos (as ls) porque findpuede hacer todo lo que necesita a través de la -printfacción:

find /path -printf '%T+ %p\n' | sort -r | head
enzotib
fuente
1
Sí, se me ocurrió, ¡ find . -type f -exec stat --format=%y \{\} \+ | sort -r | head -n1pero su solución es mucho más limpia!
Rico
3
Agregar | cut -d ' ' -f2para obtener solo el nombre del archivo
qwr
También puede seleccionar la salida de headpara incluir un cierto número de líneas. Solo necesitaba la primera línea, así que uséhead -n 1
Timmah
8

Tuve un problema similar hoy, pero lo ataqué sin él find. Necesitaba algo breve que pudiera atropellar sshpara devolver el archivo editado más recientemente en mi directorio de inicio. Esto es más o menos lo que se me ocurrió:

ls -tp | grep -v /$ | head -1

La -popción para lsagregar una barra inclinada final a los directorios, grep -velimina las líneas que terminan en una barra inclinada (también conocido como todos los directorios) y head -1limita la salida a un solo archivo.

Esto es mucho menos detallado que usar findsi todo lo que desea devolver es el nombre del archivo.

Pat Regan
fuente
Esto no maneja subdirectorios.
Clément
4

Esto está en mi sistema más rápido que printf, aunque no entiendo por qué

find /path -type f -exec stat -c "%y %n" {} + | sort -r | head
organizar
fuente
Confirmo, es más rápido.
enzotib
Un punto más, ... | sort -r | head -n1 | cut -d " " -f 4-si desea obtener solo el nombre del archivo.
皞 皞
Acabo de encontrar que sort -rserá incorrecto si existe un nombre de archivo en varias líneas.
皞 皞
2

EDITAR: Supongo que esta publicación no es "no particularmente útil" como pensé que era. Esta es una solución realmente rápida que solo realiza un seguimiento del archivo modificado más recientemente (en lugar de ordenar la lista completa de archivos):

find . -type f -printf '%T@ %p\n' | awk 'BEGIN { mostrecenttime = 0; mostrecentline = "nothing"; } { if ($1 > mostrecenttime) { mostrecenttime = $1; mostrecentline = $0; } } END { print mostrecentline; }' | cut -f2- -d ' '

Repartidas en varias líneas para mayor claridad, se ve de la siguiente manera:

find . -type f -printf '%T@ %p\n' | awk '
    BEGIN { mostrecenttime = 0; mostrecentline = "nothing"; }
    {
        if ($1 > mostrecenttime)
            { mostrecenttime = $1; mostrecentline = $0; }
    }
    END { print mostrecentline; }' | cut -f2- -d ' '

Fin de EDITAR


No es una publicación particularmente útil, pero dado que 'organizar' estaba discutiendo la velocidad, pensé en compartir esto.

Las soluciones del arreglo y enzotib implican enumerar todos los archivos dentro del directorio con sus mtimes y luego ordenarlos. Como sabes, no es necesario ordenar para encontrar el máximo. Encontrar el máximo se puede hacer en tiempo lineal, pero la clasificación lleva n tiempo log (n) [Sé que la diferencia no es mucha, pero aún así;]]. No puedo pensar en una forma ordenada de implementar esto. [EDITAR: Una implementación ordenada (aunque sucia) y rápida proporcionada anteriormente.]

Siguiente mejor opción: para buscar el archivo editado más recientemente en un directorio, busque recursivamente el archivo editado más recientemente en cada subdirectorio de nivel 1. Deje que este archivo represente el subdirectorio. Ahora ordene los archivos de nivel 1 junto con los representantes de los subdirectorios de nivel 1. Si el número de archivos de nivel 1 y subdirectorios de cada directorio es casi constante, entonces este proceso debería escalar linealmente con el número total de archivos.

Esto es lo que se me ocurrió para implementar esto:

findrecent() { { find "$1" -maxdepth 1 -type f -exec stat -c "%y %n" {} + | sort -r | head -1 && find "$1" -mindepth 1 -maxdepth 1 -type d -exec findrecent {} \;; } | sort -r | head -1; }
findrecent .

Ejecuté esto y obtuve un montón de find: findrecent: No such file or directoryerrores. Motivo: -exec de find se ejecuta en un shell diferente. Traté de definir findrecent en .bashrc, .xsessionrc pero esto no ayudó [agradecería la ayuda aquí]. Al final recurrí a poner

#!/bin/bash
{ find "$1" -maxdepth 1 -type f -exec stat -c "%y %n" {} + | sort -r | head -1 && find "$1" -mindepth 1 -maxdepth 1 -type d -exec findrecent {} \;; } | sort -r | head -1;

en un script llamado findrecenten mi RUTA y luego ejecutarlo.

Ejecuté esto, seguí esperando y esperando sin salida. Solo para asegurarme de que no estaba lidiando con ningún bucle infinito, modifiqué el archivo a

#!/bin/bash
echo "$1" >&2
{ find "$1" -maxdepth 1 -type f -exec stat -c "%y %n" {} + | sort -r | head -1 && find "$1" -mindepth 1 -maxdepth 1 -type d -exec findrecent {} \;; } | sort -r | head -1;

e intenté de nuevo. Funcionó, pero tardó 1 minuto y 35 segundos en mi carpeta personal, ¡las soluciones de Arreglo y enzotib tomaron 1.69, 1.95 segundos respectivamente!

¡Tanto por la superioridad de O (n) sobre O (n log (n))! ¡Maldita sea tu función, llamada sobrecarga! [O más bien, sobrecarga de llamadas de script]

Pero este script escala mejor que las soluciones anteriores y apuesto a que se ejecutará más rápido que ellos en el banco de memoria de Google; D

S Prasanth
fuente
2

Usar perlen conjonctina con find:

 find my_directory -type f -printf '%T@\t%p\n' | perl -ane '@m=@F if ($F[0]>$m[0]); END{print $m[1];}'

Obtiene el nombre del archivo con la mayor época == último archivo modificado.

MUY Bélgica
fuente
1

No está tan de moda, pero también es posible lograrlo con Midnight Commander : busque *, panelice el resultado, ordene por tiempo de modificación en orden inverso.

Obviamente, es un poco más lento que find: mi directorio de inicio, que contiene 922000 archivos, se ordenó mcen casi 14 minutos, mientras que findpasé menos de 5, pero hay algunos beneficios:

  • Probablemente pasaría más tiempo que la diferencia de 9 minutos inventando una invocación de búsqueda adecuada :)

  • menos posibilidades de error (olvidé especificar -r para ordenar, etc., comience de nuevo)

  • es posible jugar con el conjunto de resultados cambiando el orden de clasificación, etc., sin volver a consultar los archivos.

  • es posible realizar operaciones de archivo solo en algunos archivos del conjunto de resultados, es decir, ordenar por tamaño, eliminar algunos archivos grandes que no son necesarios

Sergey
fuente