Analisando a saída de ls
não é confiável .
Em vez disso, use find
para localizar os arquivos e sort
para encomendá-los por timestamp. Por exemplo:
while IFS= read -r -d $'#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'move-oldest /mnt/backup/ /var/log/foo/ 20
' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %pwhile IFS= read -r -d $'#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'move-oldest /mnt/backup/ /var/log/foo/ 20
' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p%pre%' \
2>/dev/null | sort -z -n)
' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p%pre%' \
2>/dev/null | sort -z -n)
' \
2>/dev/null | sort -z -n)
' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p%pre%' \
2>/dev/null | sort -z -n)
O que tudo isso está fazendo?
Primeiro, os comandos find
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:
- Um registro de data e hora
- Um espaço
- O caminho relativo para o arquivo
- Um caractere NULL
O registro de data e hora é importante. O %T@
do especificador de -printf
divide em T
, o que indica "Hora da última modificação" do arquivo (mtime) e @
, que indica "Segundos desde 1970", incluindo os segundos fracionários.
O espaço é meramente um delimitador arbitrário. O caminho completo para o arquivo é para que possamos nos referir a ele mais tarde, e o caractere NULL é um terminador porque é um caractere ilegal em um nome de arquivo e, portanto, nos informa com certeza que chegamos ao final do caminho para o arquivo. arquivo.
Eu incluí o 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 serem excluídos sejam suprimidas.
O resultado do comando find
é uma lista de todos os diretórios no diretório atual. A lista é canalizada para sort
, que é instruído para:
-
-z
Trate NULL como o caractere terminador de linha em vez de nova linha. -
-n
Ordenar numericamente
Como os segundos desde 1970 sempre sobem, queremos o arquivo cujo registro de data e hora seja o menor número. O primeiro resultado de sort
será a linha que contém o menor registro de data e hora numerado. Tudo o que resta é extrair o nome do arquivo.
Os resultados do find
, sort
pipeline são transmitidos por meio de substituição de processos para while
onde é lido como se fosse um arquivo em stdin. while
invoca read
para processar a entrada.
No contexto de read
, definimos a variável IFS
como nada, o que significa que o espaço em branco não será interpretado de forma inadequada como um delimitador. read
é informado -r
, que desativa a expansão de escape e -d $'
, o que torna o delimitador de fim de linha NULL correspondente à saída de nosso find
'sort
, line
pipeline.
O primeiro bloco de dados, que representa o caminho de arquivo mais antigo, precedido por seu registro de data e hora e espaço, é lido na variável #*
. Em seguida, a substituição de parâmetros é usada com a expressão $file
, que simplesmente substitui todos os caracteres do início da string até o primeiro espaço, incluindo o espaço, sem nada. Isso remove o registro de data e hora da modificação, deixando apenas o caminho completo para o arquivo.
Neste ponto, o nome do arquivo é armazenado em $file
e você pode fazer o que quiser com ele. Quando terminar de fazer algo com while
, a instrução read
fará um loop e o comando ls -t
será executado novamente, extraindo o próximo trecho e o próximo nome de arquivo.
Não existe uma maneira mais simples?
Não. Formas mais simples são bugs.
Se você usar head
e pipe para tail
ou mv $(anything)
(ou qualquer coisa ), você quebrará arquivos com novas linhas nos nomes dos arquivos. Se você mv "$(anything)"
, os arquivos com espaço em branco no nome causarão a quebra. Se você read
, os arquivos com novas linhas no nome causarão a quebra. Se você -d $'
sem /var/log/foo/
'/mnt/backup/
, então você quebrará arquivos com espaço 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 em scripts, se puder evitar fazê-lo.
Solução
%pre%Ligue para:
%pre% Para mover os 20 arquivos mais antigos de -type f
para find
.
Note que estou incluindo os diretórios e . Para arquivos, apenas adicione %code% à invocação %code% .
Obrigado
Graças a enzotib e Павел Танков para melhorias nesta resposta.