Como encontrar recursivamente o arquivo modificado mais recente em um diretório?

Respostas:

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

Para uma árvore enorme, pode ser difícil sortmanter tudo na memória.

%T@fornece o tempo de modificação, como um carimbo de data / hora unix, sort -nclassifica numericamente, tail -1pega a última linha (carimbo de data / hora mais alta), cut -f2 -d" "corta o primeiro campo (o carimbo de data / hora) da saída.

Edit: Assim como -printfprovavelmente é apenas o GNU, stat -ctambém é possível o uso de um novo. Embora seja possível fazer o mesmo no BSD, as opções de formatação são diferentes ( -f "%m %N"ao que parece)

E eu perdi a parte do plural; se você quiser mais do que o arquivo mais recente, basta aumentar o argumento da cauda.

plundra
fonte
7
Se a ordem importa, você pode alternar o uso sort -rn | head -3em vez de sort -n | tail -3. Uma versão fornece os arquivos do mais antigo para o mais novo, enquanto a outra passa do mais recente para o mais antigo.
Don Faulkner
3
Eu tinha um diretório enorme (cerca de dez mil arquivos pequenos) e estava preocupado com o desempenho, mas ... esse comando é executado em menos de um segundo! Ótimo, muito obrigado !!! :-)
lucaferrario
2
"Para uma árvore enorme, pode ser difícil para uma espécie guardar tudo na memória". sortcriará arquivos temporários (in /tmp) conforme necessário, então não acho que seja uma preocupação.
Vladimir Panteleev
1
modificá-lo para: -printf '%T@ %Tb %Td %TY %p\n'vai dar-lhe um carimbo de data (se necessário) (semelhante a ls)
bshea
1
Eu acho o seguinte mais curto e com saída mais interpretável:find . -type f -printf '%TF %TT %p\n' | sort | tail -1
snth 7/17/17
129

Seguindo a resposta da @ plundra , aqui está a versão BSD e OS X:

find . -type f -print0 | xargs -0 stat -f "%m %N" |
sort -rn | head -1 | cut -f2- -d" "
Emerson Farrugia
fonte
1
O BSD / OS X findsuporta, em +vez de \;? Porque isso faz a mesma coisa (passando vários arquivos como parâmetros), sem o -print0 | xargs -0pipe.
DevSolar 25/08
O que fazer se eu quiser que os últimos 5 ou n número de arquivos sejam modificados em ordem decrescente?
khunshan
@khunshan altere o head -1parahead -5
Emerson Farrugia
20

Em vez de classificar os resultados e manter apenas os últimos modificados, você pode usar o awk para imprimir apenas aquele com maior tempo de modificação (em tempo unix):

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

Essa deve ser uma maneira mais rápida de resolver seu problema se o número de arquivos for grande o suficiente.

Eu usei o caractere NUL (ou seja, \ 0 ') porque, teoricamente, um nome de arquivo pode conter qualquer caractere (incluindo espaço e nova linha), exceto isso.

Se você não possui esses nomes de arquivos patológicos em seu sistema, também pode usar o caractere de nova linha:

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

Além disso, isso também funciona em mawk.

marco
fonte
Isso pode ser facilmente adaptado para manter os três mais recentes.
Pausado até novo aviso.
1
Isso não funciona com mawka alternativa padrão do Debian.
Janeiro
Não, mas, nesse caso, você pode usar o caractere de nova linha se ele não incomodá-lo;)
marco
10

Ocorreu um problema ao encontrar o último arquivo modificado no Solaris 10. Não findexiste a printfopção e statnão está disponível. Eu descobri a seguinte solução que funciona bem para mim:

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

Para mostrar o nome do arquivo, use

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

Explicação

  • find . -type f localiza e lista todos os arquivos
  • sed 's/.*/"&"/' agrupa o nome do caminho entre aspas para lidar com espaços em branco
  • xargs ls -Eenvia o caminho entre aspas para ls, a -Eopção garante que um registro de data e hora completo (formato ano-mês-dia hora-minuto-segundos-nanossegundos ) seja retornado
  • awk '{ print $6," ",$7 }' extrai apenas data e hora
  • awk '{ print $6," ",$7," ",$9 }' extrai data, hora e nome do arquivo
  • sort retorna os arquivos classificados por data
  • tail -1 retorna apenas o último arquivo modificado
Florian Feldhaus
fonte
9

Isso parece funcionar bem, mesmo com subdiretórios:

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

No caso de muitos arquivos, refine a localização.

mgratia
fonte
1
A -lopção de lsparece desnecessária. Apenas -trparece suficiente.
Acumenus
6
Este parece classificar por diretório, por isso não irá necessariamente mostrar o arquivo mais recente
Fabian Schmengler
1
No caso de existirem espaços nos caminhos de arquivo melhor fazer:find . -type f -print0 | xargs -0 ls -ltr | tail -n 1
Manutenção periódica
Isso não encontra os arquivos mais recentes para mim.
K.-Michael Aye
1
Acho que esta quebra se houver espaços em nomes de arquivos
waferthin
7

Mostra o arquivo mais recente com carimbo de data / hora legível por humanos:

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

O resultado fica assim:

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

Para mostrar mais arquivos, substitua -n1por um número maior

Fabian Schmengler
fonte
5

Eu uso algo semelhante o tempo todo, bem como a lista top-k dos arquivos modificados mais recentemente. Para árvores de diretório grandes, pode ser muito mais rápido evitar a classificação . No caso dos top-1 arquivo modificado mais recentemente:

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

Em um diretório que contém 1,7 milhão de arquivos, recebo o mais recente do 3.4s, uma aceleração de 7.5x em relação à solução 25.5s usando sort.

Pierre D
fonte
Muito legal: eu troquei a última impressão com: system ("ls -l $ f") se eof () para ver a data de uma maneira agradável também.
Martin T.
@MartinT. : ótimo, e você é bem-vindo. É estranho para mim que as pessoas tenham esse instinto de classificar as coisas (O (n log n)) quando um método O (n) está disponível. Essa parece ser a única resposta para evitar a classificação. BTW, o objetivo do comando que sugeri é apenas encontrar o caminho do arquivo mais recente. Você poderia alias o comando no seu shell (por exemplo, como lastfile) e, em seguida, você pode fazer o que quiser com o resultado, tais como ls -l $(lastfile .), ou open $(lastfile .)(em um Mac), etc.
Pierre D
Ah, estou corrigido: vejo outra resposta abaixo (@marco). +1.
Pierre D
4

Isso fornece uma lista classificada:

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

Inverta a ordem colocando um '-r' no comando de classificação. Se você quiser apenas nomes de arquivos, insira "awk '{print $ 11}' |" antes de '| cabeça'

Karlo
fonte
3

No Ubuntu 13, o seguinte é feito, talvez um pouco mais rápido, pois inverte o tipo e usa 'head' em vez de 'tail', reduzindo o trabalho. Para mostrar os 11 arquivos mais recentes em uma árvore:

encontrar . -tipo f -printf '% T @% p \ n' | ordenar -n -r | cabeça -11 | corte -f2- -d "" | sed -e 's, ^. / ,,' | xargs ls -U -l

Isso fornece uma lista completa de ls sem reclassificar e omite o irritante './' que 'find' coloca em cada nome de arquivo.

Ou, como uma função 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
}

Ainda assim, a maior parte do trabalho foi realizada pela solução original da plundra. Graças plundra.

RickyS
fonte
3

Eu enfrentei o mesmo problema. Preciso encontrar o arquivo mais recente recursivamente. find levou cerca de 50 minutos para encontrar.

Aqui está um pequeno script para fazê-lo mais rápido:

#!/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

É uma função recursiva que obtém o item modificado mais recente de um diretório. Se este item for um diretório, a função é chamada recursivamente e pesquisa nesse diretório, etc.

AnatomicJC
fonte
3

Acho o seguinte mais curto e com saída mais interpretável:

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

Dado o comprimento fixo das datas padronizadas no formato ISO, a classificação lexicográfica é boa e não precisamos da -nopção na classificação.

Se você deseja remover os carimbos de data e hora novamente, pode usar:

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

Se a execução statde cada arquivo individualmente for lenta, use-a xargspara acelerar um pouco as coisas:

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

Isso altera recursivamente o tempo de modificação de todos os diretórios no diretório atual para o arquivo mais recente em cada diretório:

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
fonte
1
Ele quebra muito se algum diretório contém espaços - precisa definir o IFS e usar aspas: IFS = $ '\ n'; para o diretório em $ (find ./-type d); faça eco de "$ dir"; encontre o tipo "$ dir" f -printf '% T @ "% p" \ n' | classificar -n | cauda -1 | corte -f2- -d "" | xargs -I {} toque em -r {} "$ dir"; feito;
Andy Lee Robinson
2

Este CLI simples também funcionará:

ls -1t | head -1

Você pode alterar o -1 para o número de arquivos que deseja listar

Ankit Zalani
fonte
10
Não, não será, pois não é recursivo.
Arataj
1

Achei o comando acima útil, mas, para o meu caso, eu precisava ver a data e a hora do arquivo e também tive um problema com vários arquivos com espaços nos nomes. Aqui está a minha solução de trabalho.

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

O seguinte comando funcionou no Solaris:

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

Ignorando arquivos ocultos - com carimbo de tempo agradável e rápido

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

Resultado

Manipula bem os espaços nos nomes de arquivos - não que eles devam ser usados!

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

Mais

Mais find abundância seguindo o link.

Serge Stroobandt
fonte
1

Para procurar arquivos no diretório / target_directory e todos os seus subdiretórios, que foram modificados nos últimos 60 minutos:

$ find /target_directory -type f -mmin -60

Para encontrar os arquivos modificados mais recentemente, classificados na ordem inversa do tempo de atualização (ou seja, os arquivos atualizados mais recentemente):

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

Eu prefiro este, é mais curto:

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

Eu escrevi um pacote pypi / github para esta pergunta porque também precisava de uma solução.

https://github.com/bucknerns/logtail

Instalar:

pip install logtail

Uso: caudas arquivos alterados

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

Uso2: Abre o último arquivo alterado no editor

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