Como escrevo um script para mover apenas os 20 arquivos mais antigos de uma pasta para outra? Existe uma maneira de pegar os arquivos mais antigos em uma pasta?
bash
shell
shell-script
files
timestamps
user11598
fonte
fonte
atime
(último acesso),ctime
(última alteração de permissão) emtime
(última modificação) ... por exemplo.ls -t
e da descobertaprintf "%T"
usomtime
... Parece, de acordo com este link , que minhasext4
partições são capazes de lidar com uma data de criação, masls
efind
estat
não têm as opções apropriadas (ainda) ...Respostas:
A análise da saída de não
ls
é confiável .Em vez disso, use
find
para localizar os arquivos esort
ordená-los por carimbo de data / hora. Por exemplo:O que tudo isso está fazendo?
Primeiro, os
find
comandos localizam todos os arquivos e diretórios no diretório atual (.
), mas não nos subdiretórios do diretório atual (-maxdepth 1
), depois imprimem:O registro de data e hora é importante. O
%T@
especificador de formato para-printf
divide emT
, que indica "Hora da última modificação" do arquivo (mtime) e@
que indica "Segundos desde 1970", incluindo segundos fracionários.O espaço é apenas um delimitador arbitrário. O caminho completo para o arquivo é para que possamos consultá-lo mais tarde, e o caractere NULL é um terminador, pois é um caractere ilegal em um nome de arquivo e, portanto, nos permite saber com certeza que chegamos ao final do caminho para o arquivo Arquivo.
Eu incluí
2>/dev/null
para que os arquivos que o usuário não tem permissão para acessar sejam excluídos, mas as mensagens de erro sobre eles sendo excluídos sejam suprimidas.O resultado do
find
comando é uma lista de todos os diretórios no diretório atual. A lista é canalizada para asort
qual é instruído a:-z
Trate NULL como o caractere terminador de linha em vez de nova linha.-n
Classificar numericamenteComo os segundos desde 1970 sempre aumentam, queremos o arquivo cujo carimbo de data e hora foi o menor número. O primeiro resultado de
sort
será a linha que contém o menor carimbo de data e hora numerado. Tudo o que resta é extrair o nome do arquivo.Os resultados do
find
,sort
encanamento é passado através de substituição processo parawhile
onde se lê como se fosse um arquivo em stdin.while
por sua vez, invocaread
para processar a entrada.No contexto
read
, definimos aIFS
variável como zero, o que significa que o espaço em branco não será interpretado inadequadamente como um delimitador.read
é contada-r
, que desativa a expansão fuga, e-d $'\0'
, o que torna o delimitador NULL fim-de-linha, combinando a saída do nossofind
,sort
pipeline.O primeiro pedaço de dados, que representa o caminho do arquivo mais antigo, precedido por seu carimbo de data e hora e um espaço, é lido na variável
line
. Em seguida, a substituição de parâmetro é usada com a expressão#*
, que simplesmente substitui todos os caracteres desde o início da string até o primeiro espaço, incluindo o espaço, por nada. Isso retira o registro de data e hora da modificação, deixando apenas o caminho completo para o arquivo.Nesse ponto, o nome do arquivo está armazenado
$file
e você pode fazer o que quiser com ele. Quando você terminar de fazer algo com$file
awhile
instrução, o loopread
será executado e o comando será executado novamente, extraindo o próximo pedaço e o próximo nome do arquivo.Não existe uma maneira mais simples?
Não. Maneiras mais simples são problemáticas.
Se você usar o
ls -t
pipe parahead
outail
(ou qualquer outra coisa ), interromperá os arquivos com novas linhas nos nomes dos arquivos. Se você,mv $(anything)
então, arquivos com espaço em branco no nome causarão falhas. Se você,mv "$(anything)"
então, os arquivos com novas linhas à direita no nome causarão interrupções. Se vocêread
não,-d $'\0'
então você quebrará arquivos com espaços em branco em seus nomes.Talvez em casos específicos, você tenha certeza de que uma maneira mais simples é suficiente, mas nunca deve escrever suposições como essa nos scripts, se puder evitar fazê-lo.
Solução
Ligue como:
Para mover os 20 arquivos mais antigos de
/var/log/foo/
para/mnt/backup/
.Observe que estou incluindo arquivos e diretórios. Para arquivos, adicione apenas
-type f
afind
chamada.obrigado
Obrigado ao enzotib e ao Павел Танков pelas melhorias nesta resposta.
fonte
-n
. Pelo menos na minha versão, ele não classifica os números decimais corretamente. Você precisa remover o ponto na data ou usar-printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz
, datas ISO ou outra coisa..
deve ser irrelevante para você.) Seria mais claro dizersort -z -n -t. -k1
.%TS
também mostra uma "parte fracionária" que estaria no formato00.0000000000
, para que você também perca granularidade. O GNU recentesort
pode resolver esse problema usando-V
uma "classificação de versão", que manipulará esse tipo de ponto flutuante conforme o esperado.%T@
também funcionaria, pois é preenchida com zero.É mais fácil no zsh, onde você pode usar o
Om
qualificador glob para classificar correspondências por data (a mais antiga primeiro) e o[1,20]
qualificador para manter apenas as 20 primeiras correspondências:Adicione o
D
qualificador se você quiser incluir arquivos de ponto também. Adicione.
se você deseja corresponder apenas arquivos regulares e não diretórios.Se você não possui o zsh, aqui está um liner de Perl (você pode fazê-lo em menos de 80 caracteres, mas com uma despesa adicional em clareza):
Com apenas ferramentas POSIX ou até bash ou ksh, classificar arquivos por data é uma dor. Você pode fazer isso facilmente
ls
, mas analisar a saída dels
é problemático; portanto, isso só funciona se os nomes de arquivos contiverem apenas caracteres imprimíveis que não sejam novas linhas.fonte
Combine a
ls -t
saída comtail
ouhead
.Exemplo simples, que funciona apenas se todos os nomes de arquivos contiverem apenas caracteres imprimíveis, exceto espaços em branco e
\[*?
:fonte
ls -1Atr
touch $'foo\n*'
. O que acontece se você executar o mv "$ (ls)" com esse arquivo sentado lá?Você pode usar o GNU find para isso:
Onde find imprime o tempo de modificação (em segundos de 1970) e o nome de cada arquivo do diretório atual, a saída é classificada de acordo com o primeiro campo, os 20 mais antigos são filtrados e movidos para
dest_dir
. Remova oecho
se você testou a linha de comando.fonte
Ninguém (ainda) postou um exemplo do bash que atende a caracteres de nova linha incorporados (qualquer coisa incorporada) no nome do arquivo, então aqui está um. Move os 3 arquivos regulares mais antigos (mdate)
Este é o trecho de dados de teste
Aqui está o trecho dos resultados da verificação
fonte
É mais fácil fazer com o GNU
find
. Uso-o todos os dias no meu DVR Linux para excluir gravações do meu sistema de vigilância por vídeo com mais de um dia.Aqui está a sintaxe:
Lembre-se de que
find
define um dia como 24 horas a partir do momento da execução. Portanto, os arquivos modificados pela última vez às 23:00 não serão excluídos à 01:00.Você pode até combinar
find
comcron
, para que as exclusões possam ser agendadas automaticamente executando o seguinte comando como root:Você sempre pode obter mais informações
find
consultando sua página de manual:fonte
Como as outras respostas não se encaixam no meu objetivo e no das perguntas, esse shell é testado no CentOS 7:
fonte