Exclui todos os arquivos em uma pasta, exceto o último (mais recente) 20 [duplicado]

3

Eu quero remover todos os arquivos de diretórios diferentes, e quero manter apenas os arquivos mais recentes 20 arquivos. este é o comando correto para fazer isso?

ls -t1 /mnt/dwh/ftp/dwh_ftp_cbs/ARLOGS/ | tail -n +22 | xargs rm -f
    
por Ali 30.10.2015 / 08:16

3 respostas

3

Com zsh e qualificadores glob :

print -rl -- *(D.Om[1,-21])

listará todos os arquivos regulares, exceto o último (mais recentemente modificado) 20.
D seleciona arquivos ocultos, . seleciona apenas arquivos regulares, Om significa ordenação inversa por mtime (mais antigo primeiro) e [1,-21] seleciona do primeiro até o vigésimo ao último.
Se estiver satisfeito com o resultado, substitua print -rl por rm :

rm -- *(D.Om[1,-21])

Se você tiver um grande número de arquivos, talvez seja necessário usar zargs para evitar que a lista de argumentos seja muito longa :

autoload zargs
zargs ./*(D.Om[1,-21]) -- rm
    
por 30.10.2015 / 11:35
2

Em seu comando há muitas desvantagens, por exemplo, o processamento da saída de ls , é uma espécie de catecismo. Veja este artigo por que não fazer isso.

Esta abordagem usa extensões no do GNU e é testada em bash, zsh, ksh93, dash e ash:

for f in * .*; do 
  [ -f "$f" ] && printf "%s " $(perl -e 'print((stat("$ARGV[0]"))[9])' -- "$f") && \
  printf "%s
touch -- '--a'"'"'b"c
d$e\nf\r!g;h^i'j(k)l*m%n=o?p.txt'
" "$f"; done | tr '
touch '\'"'"'echo test '"'"'\'
\n' '\n
for f in * .*; do 
  [ -f "$f" ] && printf "%s " $(perl -e 'print((stat("$ARGV[0]"))[9])' -- "$f") && \
  printf "%s
touch -- '--a'"'"'b"c
d$e\nf\r!g;h^i'j(k)l*m%n=o?p.txt'
" "$f"; done | tr '
touch '\'"'"'echo test '"'"'\'
\n' '\n%pre%' | sort -k1nr | \ sed "1,20d;s/'/'\"'\"'/g;s/[^ ]* \(.*\)/rm -f -- '';/" | tr '%pre%' '\n' | sh
' | sort -k1nr | \ sed "1,20d;s/'/'\"'\"'/g;s/[^ ]* \(.*\)/rm -f -- '';/" | tr '%pre%' '\n' | sh

Este é o nome de arquivo mais estranho que consegui produzir, e funciona mesmo assim:

%pre%

Ou um nome de arquivo que tenta "pular":

%pre%     
por 30.10.2015 / 09:28
2
ls -t1 /mnt/dwh/ftp/dwh_ftp_cbs/ARLOGS/ | tail -n +22 | xargs rm -f

Não funcionará porque a saída de ls incluirá apenas os nomes dos arquivos, não o caminho completo. Ele também ignora arquivos cujo nome começa com . .

cd /mnt/dwh/ftp/dwh && ls -At1 | tail -n +21 | xargs rm -f --

resolveria isso, mas você ainda teria problemas com nomes de arquivos que contêm espaços em branco ou nova linha ou apóstrofo ou barras invertidas ou aspas duplas (e possivelmente nomes de arquivos com bytes que não formam caracteres válidos na localidade).

(export LC_ALL=C
 cd /mnt/dwh/ftp/dwh && ls -At1 |
   tail -n +21 |
   sed 's/"/\"/g;s/.*/"&"/' |
   xargs rm -f --
)

consertaria a maioria deles, mas você ainda teria um problema com nomes de arquivos contendo caracteres de nova linha. O problema é que a saída de ls -At1 não é pós-processável.

A saída de ls -Atd1 ./.* ./* pode ser pós-processada (como os prefixos ./ indicam onde cada nome de arquivo é iniciado na saída, mas não facilmente), mas você corre o risco de atingir o limite do número de argumentos passados para um comando, passando a lista de nomes de arquivos para ls assim.

O melhor seria usar um shell que possa fazer essa classificação em seus globs ou confiar nas extensões do GNU se o seu só precisa trabalhar em sistemas GNU:

 cd /mnt/dwh/ftp/dwh &&
   find . ! -name . -prune ! -type d -printf '%T@\t%p
eval "set -- $(COLUMNS=4294967295 ls -Atx --quoting-style=shell-always)"
if [ "$#" -gt 20 ]; then
  shift 20
  printf '%s
ls -t1 /mnt/dwh/ftp/dwh_ftp_cbs/ARLOGS/ | tail -n +22 | xargs rm -f
' "$@" | xargs -r0 rm -f -- fi
' | tr '
cd /mnt/dwh/ftp/dwh && ls -At1 | tail -n +21 | xargs rm -f --
\n' '\n
(export LC_ALL=C
 cd /mnt/dwh/ftp/dwh && ls -At1 |
   tail -n +21 |
   sed 's/"/\"/g;s/.*/"&"/' |
   xargs rm -f --
)
' | sort -rn | tail -n +21 | cut -f 2- | tr '
 cd /mnt/dwh/ftp/dwh &&
   find . ! -name . -prune ! -type d -printf '%T@\t%p
eval "set -- $(COLUMNS=4294967295 ls -Atx --quoting-style=shell-always)"
if [ "$#" -gt 20 ]; then
  shift 20
  printf '%s%pre%' "$@" | xargs -r0 rm -f --
fi
' | tr '%pre%\n' '\n%pre%' | sort -rn | tail -n +21 | cut -f 2- | tr '%pre%\n' '\n%pre%' | xargs -r0 rm -f
\n' '\n%pre%' | xargs -r0 rm -f

Aqui, estamos excluindo o arquivo do tipo diretório , que afetará a numeração, mas provavelmente está mais próximo do que você deseja, pois não suponho que você queira remover diretórios. Se você deseja remover diretórios, remova o -type d e adicione a opção -r a rm .

Ou:

%pre%

Ou use uma linguagem de programação genérica como perl , python , ruby ...

    
por 30.10.2015 / 13:20