Arquiva o arquivo mais antigo, onde os arquivos são nomeados de acordo com seu timestamp

3

Contexto

Eu tenho scripts de backup em execução que criam arquivos tar.gz e os transferem para uma pasta de dropbox na minha VM headless linux.

Problema

Eu não sei como criar um script que irá detectar o arquivo mais antigo entre os arquivos nomeados de acordo com o esquema /root/Dropbox/apache2-backup-%Y-%m-%d-%H-%M .

Eu, então, quero mover este arquivo para uma pasta /root/Dropbox-archive/ , porque há apenas 2 GB de espaço no Dropbox.

    
por Alex Stewart 24.10.2015 / 02:17

4 respostas

2

Solução "simples"

ls -1 /Dropbox/apache2-backup-* deve ordenar alfabeticamente por nome, por exemplo, por data (uma vez que a data é formatada com o dígito mais significativo à esquerda). Você pode, então, obter o mais antigo, canalizando para head -1 (supondo que não haja novas linhas nos nomes dos arquivos). Geralmente, canalizar a partir de ls é uma má ideia, mas deve estar bem, neste caso, já que você sabe que não há caracteres especiais nos nomes dos arquivos.

mv "$(ls -1 /Dropbox/apache2-backup-* | head -1)" /root/Dropbox-archive/

Eu provavelmente também recomendaria usar mv --no-clobber (ou mv -n ), para que você não sobrescreva arquivos acidentalmente.

Solução "adequada"

Se você estiver realmente preocupado com caracteres especiais, use o seguinte em seu lugar.

to_move="$(find /Dropbox -maxdepth 1 -type f -name 'apache2-backup-*' -print0 | sort -z | tr '
mv "$(find /Dropbox -maxdepth 1 -type f -name 'apache2-backup-*' -print0 | sort -z | tr '
mv "$(ls -1 /Dropbox/apache2-backup-* | head -1)" /root/Dropbox-archive/
\n' '\n
to_move="$(find /Dropbox -maxdepth 1 -type f -name 'apache2-backup-*' -print0 | sort -z | tr '
mv "$(find /Dropbox -maxdepth 1 -type f -name 'apache2-backup-*' -print0 | sort -z | tr '%pre%\n' '\n%pre%' | head -1 | tr '%pre%\n' '\n%pre%')" /root/Dropbox-archive/
\n' '\n%pre%' | head -1 | tr '%pre%\n' '\n%pre%')" mv "$to_move" /root/Dropbox-archive/
' | head -1 | tr '%pre%\n' '\n%pre%')" /root/Dropbox-archive/
\n' '\n%pre%' | head -1 | tr '%pre%\n' '\n%pre%')" mv "$to_move" /root/Dropbox-archive/

Explicação

  • find /Dropbox -maxdepth 1 -type f -name 'apache2-backup-*' -print0 : retorna arquivos normais ( -type f ) em /Dropbox sem descer para subdiretórios ( -maxdepth 1 ), que correspondem ao padrão -name 'apache2-backup-*' . Delimite por caractere nulo, em vez de novas linhas -print0 .
  • | sort -z : classifique com base no caractere nulo (N.B. nem todos os tipos podem fazer isso)
  • | tr '| head -1\n' '\n| tr 'mv "$to_move" /root/Dropbox-archive/\n' '\n%code%'' : troca null e newlines para processamento por cabeçalho.
  • %code% : retorna a primeira linha.
  • %code% : swap null e newlines de volta.
  • %code% : faça o movimento!

Ou, em uma única linha:

%pre%     
por 24.10.2015 / 03:08
2

Se você está apenas movendo o mais antigo , use este:

_mv(){ mv -- "$1" /root/Dropbox-archive/; }
_mv /Dropbox/apache2-backup-*

Mas o seu título não diz isso.

Como processar ls output é uma má ideia , aqui está uma abordagem melhor:

if [ "$BASH" ]; then
# If you happen to use bash or any other shell that has some similar array:
move_first_things(){ mv -- "${@:0:50}" /root/Dropbox-archive/; }
else # Unfortunate POSIX way
# As long as copy-pasting is acceptable, loop-unrolling with
# 'mv -- "$1" "$2" "$3"... /root/Dropbox-archive/' is better since it calls 'mv' less.
move_first_things(){
    local max=50 count=0
    while [ "$count" -lt "$max" ]; do
        mv -- "$1" /root/Dropbox-archive/
        shift
        : $((count = count + 1))
    done
}
fi

E como a globbing classifica a saída como ls :

move_first_things /Dropbox/apache2-backup-*

PS : Isso pode ser feito com programas externos (mikeserv, explicação aqui ) . Com um pouco mais de cuidado e obfuscação extra, as pessoas podem fazê-lo com segurança. Mas eu ainda vou usar o nome do arquivo legal neste caso.

PPS : don_crisst mencionou algum zshism. O zsh tem um bom suporte para coisas anônimas, como funções, expansões de parâmetros e matrizes. Ainda usando o caminho do nome do arquivo (já que bash não tem tais operadores de classificação), a coisa toda pode ser escrita como baks=( /Dropbox/apache2-backup-* ); mv "$baks" /root/Dropbox-archive/ (referenciando um array como este simplesmente dá seu primeiro membro), ou por várias coisas, mv "${baks[@]:0:50}" /root/Dropbox-archive/ . Bem, isso parece um pouco melhor do que as funções do wrapper, na verdade.

    
por 24.10.2015 / 02:35
1
(   IFS=/                                      # split on /
    set -ef /Dropbox/apache2-backup*           # set arg array to last glob
    for    f in    $(\ls -rtd "$@";echo /)     # iterate over sorted/split array
    do     [ -z "${f##D*}" ]  ||               # ignore dirname
           mv "${1%/*}/${f%?}" \
              "/root${1%/*}-archive"           # do sorted mv
    done                                       # do && break to do only one mv
)

Por alguma razão, parece que as pessoas acham que você não pode classificar com segurança uma lista de argumentos com ls . Eu acho que isso é porque essas mesmas pessoas estão tentando delimitar nomes de caminho com novas linhas. Mas a coisa é, os nomes de caminho não delimitam em novas linhas - eles nunca têm. Os nomes de caminho delimitam / - que é o delimitador do nome do caminho e, portanto, as novas linhas apenas confundem as coisas. Se você deseja analisar de forma confiável ls output, é necessário dividi-lo corretamente.

    
por 25.10.2015 / 14:34
1

A pergunta especificou o arquivo mais antigo. ls -rt lista os arquivos classificados por hora de modificação, o mais antigo primeiro. Essa resposta não depende do formato do nome do arquivo, exceto pelo prefixo que identifica um arquivo de backup. A parte da data do nome do arquivo não é considerada aqui:

mv "$(ls -tr /root/Dropbox/apache2-backup-*| head -1)" /root/Dropbox-archive/

A classificação por hora do arquivo não é melhor que a classificação por nome de arquivo e vice-versa. É em grande parte preferência pessoal e depende da situação e da cultura.

Em ambos os casos, e mais, é mais seguro incluir a expansão de comando entre aspas duplas " como este "$( ... )" , que manipulará todos os caracteres especiais nos nomes de arquivos de arquivos de backup (espaços, pontuação, guias etc. ) exceto para nomes de arquivos contendo novas linhas.

    
por 24.10.2015 / 07:25