Obtener el archivo más reciente en un directorio en Linux

183

Buscando un comando que devolverá el archivo más reciente en un directorio.

No ver un parámetro de límite para ls...

ack
fuente
1
watch -n1 'ls -Art | tail -n 1'- muestra los últimos archivos
La mayoría de las respuestas aquí analizan el resultado lso el uso findsin el -print0cual es problemático para manejar nombres de archivos molestos. Siempre es útil mencionar: BashFAQ099 que da una respuesta POSIX a este problema
kvantour

Respuestas:

266
ls -Art | tail -n 1

No muy elegante, pero funciona.

dmckee --- gatito ex moderador
fuente
2
usando ls -Artls también puede ver la fecha del archivo.
Josir
13
Un problema menor: esta versión canaliza toda la salida de lsa tail, luego imprime solo la última línea. En mi humilde opinión, es mejor ordenar en orden ascendente y utilizar headen su lugar, como se chaossugiere. Después de imprimir la primera línea, el encabezado se cierra, por lo que enviar la siguiente línea (en realidad el siguiente bloque) generará un SIGPIPE y también se lscerrará.
Verdadero
1
@TrueY Estoy bastante de acuerdo. La respuesta del caos es mejor para la eficiencia.
dmckee --- ex-gatito moderador
2
Tenga en cuenta que esta solución incluye directorios y archivos. Esto podría ser algo bueno o malo; depende de lo que quiera el usuario individual. La razón por la que menciono esto es que la pregunta original dice "archivo".
Sildoreth
2
Creo que el beneficio previsto de usar cola en lugar de cabeza puede ser excluir la inclusión de la línea que describe el total devuelto por ls.
Peter Scott
155
ls -t | head -n1

Este comando realmente proporciona el último archivo modificado en el directorio de trabajo actual.

caos
fuente
Equivalente al mío, y posiblemente también más eficiente.
dmckee --- ex-gatito moderador
no funciona para mí, ya que echo algo de información en mi ls primero, pero obtengo el uso, ¡gracias!
ack
44
@Josir Esto se puede modificar para incluir el sello de fecha ls -tl | head -n 1, y no tiene que empujar toda la tabla a través de la tubería como lo hace la mía.
dmckee --- ex gatito moderador
1
@ noɥʇʎԀʎzɐɹƆls -t *.png | head -n1
caos
44
Esto devuelve una carpeta si es la última modificación, use: ls -Art | head -n1si desea específicamente el archivo modificado más reciente
achasinh
79

Esta es una versión recursiva (es decir, encuentra el archivo actualizado más recientemente en un directorio determinado o en cualquiera de sus subdirectorios)

find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1

Editar: usar en -f 2-lugar de -f 2lo sugerido por Kevin

gioele
fuente
55
Aquí hay una solución para Mac:find $DIR -type f -exec stat -lt "%Y-%m-%d" {} \+ | cut -d' ' -f6- | sort -n | tail -1
usuario
2
El problema es que el comando de corte trunca las rutas con espacios en lugar de -f 2 usa -f2- para devolver los campos 2 al final de la línea
Kevin
2
Como en la sugerencia de caos, si invierte el orden usando sort -nr, puede usar en head -n 1lugar de tail -n 1mejorar ligeramente la eficiencia. (Aunque si está ordenando un hallazgo recursivo, obtener la primera o la última línea no será la parte lenta).
Destenson
2
¡Muy útil! Me resulta más agradable si canalizado a ls sin embargo:find ./ -type f -printf "%T@ %p\n" -ls | sort -n | cut -d' ' -f 2- | tail -n 1 | xargs -r ls -lah
Simon
@ la versión de Mac del usuario solo ordena / muestra la fecha, no la hora. Aquí hay una versión fija:find $DIR -type f -exec stat -lt "%F %T" {} \+ | cut -d' ' -f6- | sort -n | tail -1
yoz
11

Una nota sobre confiabilidad:

Dado que el carácter de nueva línea es tan válido como cualquier otro en un nombre de archivo, cualquier solución que se base en líneas como las head/ tailbasadas son defectuosas.

Con GNU ls, otra opción es usar la --quoting-style=shell-alwaysopción y una bashmatriz:

eval "files=($(ls -t --quoting-style=shell-always))"
((${#files[@]} > 0)) && printf '%s\n' "${files[0]}"

(agregue la -Aopción lssi también desea considerar los archivos ocultos).

Si desea limitarse a archivos normales (ignorar directorios, fifos, dispositivos, enlaces simbólicos, sockets ...), deberá recurrir a GNU find.

Con bash 4.4 o posterior (para readarray -d) y GNU coreutils 8.25 o posterior (para cut -z):

readarray -t -d '' files < <(
  LC_ALL=C find . -maxdepth 1 -type f ! -name '.*' -printf '%T@/%f\0' |
  sort -rzn | cut -zd/ -f2)

((${#files[@]} > 0)) && printf '%s\n' "${files[0]}"

O recursivamente:

readarray -t -d '' files < <(
  LC_ALL=C find . -name . -o -name '.*' -prune -o -type f -printf '%T@%p\0' |
  sort -rzn | cut -zd/ -f2-)

Lo mejor aquí sería usar zshy sus calificadores globales en lugar de bashevitar toda esta molestia:

El archivo regular más nuevo en el directorio actual:

printf '%s\n' *(.om[1])

Incluidos los ocultos:

printf '%s\n' *(D.om[1])

Segundo más nuevo:

printf '%s\n' *(.om[2])

Verifique la antigüedad del archivo después de la resolución del enlace simbólico:

printf '%s\n' *(-.om[1])

Recursivamente:

printf '%s\n' **/*(.om[1])

Además, con el sistema de finalización ( compinity co) habilitado, se Ctrl+Xmconvierte en un completer que se expande al archivo más nuevo.

Entonces:

vi Ctrl+Xm

Te haría editar el archivo más nuevo (también tienes la oportunidad de verlo antes de presionar Return).

vi Alt+2Ctrl+Xm

Para el segundo archivo más nuevo.

vi * .cCtrl+Xm

para el carchivo más nuevo .

vi * (.)Ctrl+Xm

para el archivo regular más nuevo (no directorio, ni fifo / dispositivo ...), y así sucesivamente.

Stephane Chazelas
fuente
Gracias por los zshconsejos. ¿Podría proporcionar un enlace a más detalles sobre esto en zsh docs?
congelado
@freezed, mirainfo zsh qualifiers
Stephane Chazelas
Gracias @Stephane Chazelas
congelado
9

Yo suelo:

ls -ABrt1 --group-directories-first | tail -n1

Me da solo el nombre del archivo, excluyendo las carpetas.

usuario1195354
fuente
a menos que un directorio solo tenga directorios
ealfonso
8

ls -lAtr | tail -1

Las otras soluciones no incluyen archivos que comienzan con '.'.

Este comando también incluirá '.'y '..', que puede o no ser lo que desea:

ls -latr | tail -1

Jared Oberhaus
fuente
¿Volverá éste "." si el directorio ha sido modificado más recientemente que cualquier archivo contenido (digamos por una eliminación)? Tal vez ese sea el comportamiento deseado, tal vez no. Pero tienes razón en ambos sentidos.
dmckee --- ex-gatito moderador
6

Variante en corto basada en la respuesta de dmckee:

ls -t | head -1
Dmytro G. Sergiienko
fuente
pero la -1sintaxis de head ha quedado obsoleta hace bastante tiempo y -n 1debe usarse.
Tink
5

Me gusta echo *(om[1])( zshsintaxis) ya que solo da el nombre del archivo y no invoca ningún otro comando.

MikeB
fuente
1
Funciona bien si estás usando zsh. ¿No estás usando zsh? Creo que bash es el predeterminado en Ubuntu; puede instalar zsh o puede encontrar un comando equivalente para bash.
MikeB
3
El comando es "echo"; solo evalúa sus parámetros y los envía a la salida estándar. En este caso, el calificador global "om [1]" tiene "o", lo que significa ordenar los archivos coincidentes por "m", que es el tiempo modificado. Y de esa lista ordenada, tome [1], que es el primer archivo. Puede leer acerca de los calificadores glob: zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers
MikeB
4

La solución de búsqueda / clasificación funciona muy bien hasta que el número de archivos se vuelve realmente grande (como un sistema de archivos completo). Use awk en su lugar para realizar un seguimiento del archivo más reciente:

find $DIR -type f -printf "%T@ %p\n" | 
awk '
BEGIN { recent = 0; file = "" }
{
if ($1 > recent)
   {
   recent = $1;
   file = $0;
   }
}
END { print file; }' |
sed 's/^[0-9]*\.[0-9]* //'
Patricio
fuente
3

Personalmente, prefiero usar la menor cantidad de bashcomandos no incorporados que pueda (para reducir la cantidad de costosas bifurcaciones y syscalls de ejecutivos). Para ordenar por fecha se lsnecesita llamar. Pero el uso de headno es realmente necesario. Uso el siguiente one-liner (solo funciona en sistemas que admiten canalizaciones de nombres):

read newest < <(ls -t *.log)

o para obtener el nombre del archivo más antiguo

read oldest < <(ls -rt *.log)

(¡Cuidado con el espacio entre las dos marcas '<'!)

Si los archivos ocultos también son necesarios, se podría agregar un argumento.

Espero que esto pueda ayudar.

Verdad
fuente
Debe explicar que el nombre del archivo se almacena en $ más antiguo / var más reciente. Pero +1 por la menor cantidad de horquillas.
squareatom
@squareatom Pensé que leer está bastante bien integrado. Pero tienes razón, tal vez no. ¡Gracias por tu comentario!
TrueY
3
Suponiendo que sabe lo que es un incorporado, es probablemente correcto. Pero, solo estaba pensando en la pregunta del OP. Pidieron una orden que les devolviera algo. Técnicamente, su solución no genera nada. Así que simplemente agregue "&& echo $ newest" o similar al final ;-)
squareatom
3

usando la opción recursiva R ... puede considerar esto como una mejora para buenas respuestas aquí

ls -arRtlh | tail -50
mebada
fuente
2
ls -t -1 | sed '1q'

Mostrará el último elemento modificado en la carpeta. Emparejar greppara encontrar las últimas entradas con palabras clave

ls -t -1 | grep foo | sed '1q'
Richard Servello
fuente
¿Qué es el '1q'? 1es para la primera línea equiv a head -n 1lo que es q?
HattrickNZ
2

Recursivamente:

find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head
Konki
fuente
1

prueba este simple comando

ls -ltq  <path>  | head -n 1

Si desea el nombre del archivo - última modificación, ruta = /ab/cd/*.log

Si desea el nombre del directorio - última modificación, ruta = / ab / cd / * /

AGARRE DE RICHA
fuente
1

Suponiendo que no le importan los archivos ocultos que comienzan con un .

ls -rt | tail -n 1

De otra manera

ls -Art | tail -n 1
jasonleonhard
fuente
1

Con solo Bash incorporados, siguiendo de cerca BashFAQ / 003 :

shopt -s nullglob

for f in * .*; do
    [[ -d $f ]] && continue
    [[ $f -nt $latest ]] && latest=$f
done

printf '%s\n' "$latest"
Benjamin W.
fuente
0

Todas esas soluciones ls / tail funcionan perfectamente bien para archivos en un directorio, ignorando subdirectorios.

Para incluir todos los archivos en su búsqueda (recursivamente), se puede usar find. gioele sugirió ordenar la salida de búsqueda formateada. Pero tenga cuidado con los espacios en blanco (su sugerencia no funciona con espacios en blanco).

Esto debería funcionar con todos los nombres de archivo:

find $DIR -type f -printf "%T@ %p\n" | sort -n | sed -r 's/^[0-9.]+\s+//' | tail -n 1 | xargs -I{} ls -l "{}"

Esto ordena por mtime, mira man find:

%Ak    File's  last  access  time in the format specified by k, which is either `@' or a directive for the C `strftime' function.  The possible values for k are listed below; some of them might not be available on all systems, due to differences in `strftime' between systems.
       @      seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
%Ck    File's last status change time in the format specified by k, which is the same as for %A.
%Tk    File's last modification time in the format specified by k, which is the same as for %A.

Así que simplemente reemplace %Tcon %Cpara ordenar por ctime.

básico6
fuente
Tiene razón sobre el problema del espacio en blanco con la sugerencia @gioele, pero solo necesita agregar un guión a la opción -f para que sea un rango para arreglar eso: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1 O alternativamente, mantenga todos excepto el primer campo: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 1 --complement | tail -n 1
Aaron
0

Encontrar el archivo más actual en cada directorio de acuerdo con un patrón, por ejemplo, los subdirectorios del directorio de trabajo que tienen un nombre que termina con "tmp" (no distingue entre mayúsculas y minúsculas):

find . -iname \*tmp -type d -exec sh -c "ls -lArt {} | tail -n 1" \;
xb.
fuente
0
ls -Frt | grep "[^/]$" | tail -n 1
João Fonseca
fuente
1
Hay otras respuestas que proporcionan la pregunta del OP, y se publicaron hace muchos años. Cuando publique una respuesta, asegúrese de agregar una nueva solución o una explicación sustancialmente mejor, especialmente al responder preguntas anteriores.
help-info.de
2
@ help-info.de Por favor, no vote para eliminar las respuestas que son solo de código. Puede que no sean geniales, pero la eliminación es para cosas que no son respuestas
Machavity
@Machavity - Espero no haber fallado e hice un comentario solo por respuesta tardía.
help-info.de
0

Si desea obtener el archivo modificado más reciente que también incluye subdirectorios, puede hacerlo con este pequeño oneliner:

find . -type f -exec stat -c '%Y %n' {} \; | sort -nr | awk -v var="1" 'NR==1,NR==var {print $0}' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done

Si desea hacer lo mismo no para los archivos modificados, sino para los archivos accedidos, simplemente tiene que cambiar el

% Y parámetro de la stat comando para % X . Y su comando para los archivos accedidos más recientes se ve así:

find . -type f -exec stat -c '%X %n' {} \; | sort -nr | awk -v var="1" 'NR==1,NR==var {print $0}' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done

Para ambos comandos, también puede cambiar el parámetro var = "1" si desea enumerar más de un solo archivo.

OASE Software GmbH
fuente
-1

También necesitaba hacerlo, y encontré estos comandos. estos funcionan para mí:

Si desea el último archivo por su fecha de creación en la carpeta (hora de acceso):

ls -Aru | tail -n 1  

Y si desea el último archivo que tiene cambios en su contenido (modificar el tiempo):

ls -Art | tail -n 1  
masoomeh vahid
fuente