Obter o arquivo mais recente em um diretório no Linux

182

Procurando por um comando que retorne o arquivo mais recente em um diretório.

Não está vendo um parâmetro limite para ls...

ack
fonte
1
watch -n1 'ls -Art | tail -n 1'- mostra os últimos arquivos
A maioria das respostas aqui analisa a saída lsou a utilização findsem a -print0qual é problemática para lidar com nomes de arquivos irritantes. Sempre útil mencionar: BashFAQ099, que fornece uma resposta POSIX para esse problema
kvantour

Respostas:

265
ls -Art | tail -n 1

Não é muito elegante, mas funciona.

dmckee --- gatinho ex-moderador
fonte
2
Usando ls -Artls, você também pode visualizar a data do arquivo.
Josir
13
Um pequeno problema: esta versão canaliza toda a saída de lspara taile imprime apenas a ÚLTIMA linha. IMHO, é melhor classificar em ordem crescente e usarhead , como chaossugerido. Após a impressão, o cabeçote da primeira linha é encerrado; portanto, o envio da próxima linha (na verdade, o próximo bloco) aumentará um SIGPIPE e também lsserá encerrado.
TrueY 14/11
1
@ Verdadeiramente eu concordo. A resposta do caos é melhor em termos de eficiência.
dmckee --- gatinho ex-moderador
2
Observe que esta solução inclui diretórios e arquivos. Isso pode ser uma coisa boa ou ruim; depende do que o usuário individual deseja. A razão de eu trazer isso à tona é que a pergunta original diz "arquivo".
Sildoreth 13/03/2015
2
Penso que o benefício pretendido de usar cauda em vez de cabeça pode ser excluir a inclusão da linha que descreve o total retornado pelo ls.
Peter Scott
155
ls -t | head -n1

Na verdade, este comando fornece o arquivo modificado mais recente no diretório de trabalho atual.

caos
fonte
Equivalente ao meu, e possivelmente mais eficiente também.
dmckee --- gatinho ex-moderador
não funciona muito bem para mim desde que eu eco algumas informações no meu sl primeiro, mas eu recebo o uso, obrigado!
Ack
4
@ Josir Isso pode ser modificado para incluir o carimbo de data ls -tl | head -n 1e não precisa empurrar a tabela inteira pelo cano da maneira que a minha faz.
dmckee --- gatinho ex-moderador
1
@ noɥʇʎԀʎzɐɹƆls -t *.png | head -n1
caos
4
Isso retorna uma pasta, se for a última modificação, use: ls -Art | head -n1se você deseja especificamente o último arquivo modificado
achasinh
79

Esta é uma versão recursiva (ou seja, encontra o arquivo atualizado mais recentemente em um determinado diretório ou em qualquer um de seus subdiretórios)

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

Edit: use em -f 2-vez de -f 2como sugerido por Kevin

gioele
fonte
5
Aqui está uma solução para Mac:find $DIR -type f -exec stat -lt "%Y-%m-%d" {} \+ | cut -d' ' -f6- | sort -n | tail -1
user
2
O problema é que os caminhos Truncates comando cut com espaços em vez de -f 2 uso -f2- para retornar campos 2 para o fim da linha
Kevin
2
Como na sugestão do caos, se você reverter a classificação usando sort -nr, poderá usar em head -n 1vez de tail -n 1e melhorar um pouco a eficiência. (Embora se você está classificando um achado recursiva, ficando a primeira ou última linha não será a parte lenta.)
destenson
2
Muito útil! Acho que é mais amigável se canalizado para sl:find ./ -type f -printf "%T@ %p\n" -ls | sort -n | cut -d' ' -f 2- | tail -n 1 | xargs -r ls -lah
Simon
A versão para Mac do usuário classifica / exibe apenas a data, não a hora. Aqui está uma versão fixa:find $DIR -type f -exec stat -lt "%F %T" {} \+ | cut -d' ' -f6- | sort -n | tail -1
yoz
11

Uma observação sobre confiabilidade:

Como o caractere de nova linha é tão válido quanto qualquer outro no nome de arquivo, qualquer solução que dependa de linhas como o head/tail baseadas em é falha.

Com o GNU ls, outra opção é usar a --quoting-style=shell-alwaysopção e uma bashmatriz:

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

(adicione a -Aopção als se você também quiser considerar arquivos ocultos).

Se você deseja limitar a arquivos regulares (desconsiderar diretórios, fifos, dispositivos, links simbólicos, sockets ...), você precisará recorrer ao GNU find .

Com o bash 4.4 ou mais recente (para readarray -d) e o GNU coreutils 8.25 ou mais recente (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]}"

Ou 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-)

O melhor aqui seria usar zshe seus qualificadores glob em vez de bashevitar todo esse aborrecimento:

O arquivo regular mais recente no diretório atual:

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

Incluindo os ocultos:

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

Segundo mais novo:

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

Verifique a idade do arquivo após a resolução do link simbólico:

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

Recursivamente:

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

Além disso, com o sistema de conclusão ( compinite co) ativado, Ctrl+Xmtorna-se um complemento que se expande para o arquivo mais recente.

Assim:

vi Ctrl+Xm

Isso faria você editar o arquivo mais recente (você também terá a chance de ver qual arquivo antes de pressionar Return).

vi Alt+2Ctrl+Xm

Para o segundo arquivo mais recente.

vi * .cCtrl+Xm

para o carquivo mais recente .

vi * (.)Ctrl+Xm

para o arquivo regular mais recente (não diretório, nem fifo / dispositivo ...) e assim por diante.

Stephane Chazelas
fonte
Obrigado por zshdicas. Você poderia fornecer um link para mais detalhes sobre isso no zsh docs?
congelado
@freezed, vejainfo zsh qualifiers
Stephane Chazelas
Obrigado @Stephane Chazelas
congelado
9

Eu uso:

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

Dá-me apenas o nome do arquivo, excluindo pastas.

user1195354
fonte
a menos que um diretório tem apenas diretórios
ealfonso
8

ls -lAtr | tail -1

As outras soluções não incluem arquivos que começam com '.'.

Este comando também incluirá '.'e '..', que pode ou não ser o que você deseja:

ls -latr | tail -1

Jared Oberhaus
fonte
Este retornará "." se o diretório foi modificado mais recentemente do que qualquer arquivo contido (digamos, por exclusão)? Talvez esse seja o comportamento desejado, talvez não. Mas você está certo de qualquer maneira.
dmckee --- gatinho ex-moderador
6

Variante em curto com base na resposta de dmckee:

ls -t | head -1
Dmytro G. Sergiienko
fonte
mas a -1sintaxe do cabeçalho foi descontinuada há algum tempo e -n 1deve ser usada.
tink
5

Eu gosto echo *(om[1])( zshsintaxe), pois isso apenas fornece o nome do arquivo e não chama nenhum outro comando.

MikeB
fonte
1
Funciona bem se você estiver usando o zsh. Você não está usando o zsh? Eu acho que o bash é o padrão no Ubuntu; você pode instalar o zsh ou encontrar um comando equivalente para o bash.
MikeB
3
O comando é "eco"; apenas avalia seus parâmetros e os envia para a saída padrão. Nesse caso, o qualificador global "om [1]" possui "o", o que significa ordenar os arquivos correspondentes por "m", que é a hora modificada. E a partir dessa lista ordenada, pegue [1], que é o primeiro arquivo. Você pode ler sobre qualificadores glob: zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers
MikeB
4

A solução de localização / classificação funciona muito bem até o número de arquivos ficar realmente grande (como um sistema de arquivos inteiro). Use o awk para acompanhar o arquivo mais recente:

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]* //'
Patrick
fonte
3

Pessoalmente, prefiro usar o menor número possível de bashcomandos internos (para reduzir o número de syscalls de fork e exec caros). Para classificar por data, o lsnecessário para ser chamado. Mas o uso de headnão é realmente necessário. Eu uso o seguinte one-liner (funciona apenas em sistemas que suportam pipes de nomes):

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

ou para obter o nome do arquivo mais antigo

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

(Observe o espaço entre as duas marcas '<'!)

Se os arquivos ocultos também forem necessários, um arg poderá ser adicionado.

Espero que isso possa ajudar.

TrueY
fonte
Você deve explicar que o nome do arquivo está armazenado no $ mais antigo / mais novo var. Mas +1 na menor quantidade de garfos.
Squareatom 02/03
@squareatom Eu pensei que ler é razoavelmente bem conhecido embutido. Mas você está certo, talvez não. Obrigado por seu comentário!
TrueY
3
Supondo que você saiba o que é um built-in, provavelmente está correto. Mas, eu estava pensando na pergunta do OP. Eles pediram um comando que retornasse algo. Tecnicamente, sua solução não produz nada. Então, basta adicionar "&& echo $ mais novo" ou semelhante ao do final ;-)
squareatom
3

usando a opção recursiva R .. você pode considerar isso como aprimoramento para obter boas respostas aqui

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

Irá mostrar o último item modificado na pasta. Parear com greppara encontrar as entradas mais recentes com palavras-chave

ls -t -1 | grep foo | sed '1q'
Richard Servello
fonte
qual é o '1q'? 1é para a primeira linha equivalente ao head -n 1que é q?
HattrickNZ
2

Recursivamente:

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

tente este comando simples

ls -ltq  <path>  | head -n 1

Se você quiser nome do arquivo - modificado pela última vez, caminho = /ab/cd/*.log

Se desejar o nome do diretório - modificado pela última vez, caminho = / ab / cd / * /

RICHA AGGARWAL
fonte
1

Presumindo que você não se importe com arquivos ocultos que começam com um .

ls -rt | tail -n 1

De outra forma

ls -Art | tail -n 1
jasonleonhard
fonte
1

Somente com os Bash embutidos, seguindo de perto o BashFAQ / 003 :

shopt -s nullglob

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

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

Todas essas soluções ls / tail funcionam perfeitamente bem para arquivos em um diretório - ignorando subdiretórios.

Para incluir todos os arquivos em sua pesquisa (recursivamente), a localização pode ser usada. gioele sugeriu classificar a saída de localização formatada. Mas tenha cuidado com os espaços em branco (a sugestão dele não funciona com espaços em branco).

Isso deve funcionar com todos os nomes de arquivos:

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

Isso classifica por mtime, veja o homem encontrar:

%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.

Então, basta substituir %Tpor %Cpara classificar por ctime.

basic6
fonte
Você está correto sobre o problema de espaço em branco na sugestão @gioele, mas você só precisa adicionar um hífen à opção -f para definir um intervalo para corrigir isso: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1 Ou, como alternativa, mantenha tudo, exceto o primeiro campo: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 1 --complement | tail -n 1
Aaron
0

Localizando o arquivo mais atual em todos os diretórios de acordo com um padrão, por exemplo, os subdiretórios do diretório de trabalho com nome terminado em "tmp" (sem distinção entre maiúsculas e minúsculas):

find . -iname \*tmp -type d -exec sh -c "ls -lArt {} | tail -n 1" \;
xb.
fonte
0
ls -Frt | grep "[^/]$" | tail -n 1
João Fonseca
fonte
1
Existem outras respostas que fornecem a pergunta do OP e foram publicadas há muitos anos. Ao postar uma resposta, adicione uma nova solução ou uma explicação substancialmente melhor, especialmente ao responder a perguntas mais antigas.
help-info.de
2
@ help-info.de Por favor, não vote para excluir respostas que são apenas de código. Eles podem não ser grande, mas a exclusão é para coisas que não são respostas
Machavity
@ Machavity - Espero não ter falhado e fiz um comentário apenas para resposta tardia.
help-info.de
0

Se você deseja obter o arquivo alterado mais recente e também incluir subdiretórios, pode fazê-lo com este pequeno 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

Se você deseja fazer o mesmo, não para arquivos alterados, mas para arquivos acessados, basta alterar o

% Y parâmetro a partir da estatística de comando para % X . E seu comando para os arquivos acessados ​​mais recentes se parece com o seguinte:

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 os comandos, você também pode alterar o parâmetro var = "1" se desejar listar mais de um arquivo.

OASE Software GmbH
fonte
-1

Eu também precisava fazer isso e encontrei esses comandos. estes funcionam para mim:

Se você deseja o último arquivo por sua data de criação na pasta (horário de acesso):

ls -Aru | tail -n 1  

E se você quiser o último arquivo com alterações em seu conteúdo (hora da modificação):

ls -Art | tail -n 1  
masoomeh vahid
fonte