rsync mais recente x GB

8

Estou procurando um comando / script para permitir que os arquivos modificados mais recentemente (até) 10 GB sejam copiados para outro computador.

Portanto, se houver 4 arquivos de 4 GB cada, apenas 2 deles deverão ser transferidos pelo script. Se houver 12 arquivos de 1 GB de tamanho, apenas os 10 mais recentes deverão ser transferidos.

exussum
fonte
1
Não consigo pensar em nenhuma maneira de fazer isso, mas, para esclarecer sua pergunta, você realmente deseja copiar os 10 GB de arquivos modificados mais recentemente, ou qualquer conjunto de arquivos de até 10 GB? Não acredito que exista nenhuma maneira de forçar o rsync a dar prioridade aos arquivos mais recentes. A resposta mais próxima que eu posso pensar seria restringir a largura de banda a um valor conhecido (como 1 MB / segundo) e matar o rsync após o tempo suficiente para transferir x GB de dados. Não é perfeito, pois a restrição de largura de banda é um valor máximo; portanto, você não pode transferir o quanto quiser.
Johnny
o mais recente. por file mtime
exussum 23/10

Respostas:

6

Aqui está um script que faz exatamente o que você pediu.

Os requisitos

  • Os arquivos transferidos devem totalizar menos que um tamanho limite.
  • Os arquivos devem ser modificados em comparação com o destino rsync.
  • Se nem todos os arquivos puderem ser transferidos, apenas os arquivos modificados mais recentemente deverão ser selecionados.

Os detalhes

Ele é usado rsync --dry-runpara criar uma lista de arquivos que seriam transferidos (esses são os arquivos modificados). Em seguida, ele usa uma combinação de due lspara obter tamanhos de arquivo e mtime. Em seguida, ele classifica os arquivos por mtime e os faz um loop até que o tamanho total exceda um limite. Por fim, chama o rsync novamente apenas com os arquivos modificados mais recentemente e com tamanho total abaixo do limite.

O script é um pouco feio, mas funciona. Uma grande limitação é que ele deve ser executado na máquina que contém o diretório rsync from. Ele pode ser modificado para usar ssh para usar um diretório remoto, mas esse tamanho excedente é deixado para o leitor.

Finalmente, as rsyncopções são codificadas no script, mas essa é uma alteração fácil se você quiser especificá-las na linha de comando. Além disso, a matemática para calcular o tamanho é feita em bytes. Isso pode ser alterado para quilo / mega / gigabytes, modificando a chamada para du e reduzindo o limite pelo mesmo fator.

Uso

./rsyncrecent.sh rsync-from-directory rsync-to-directory

onde rsync-from-directoryé um diretório local e rsync-to-directoryqualquer diretório local ou remoto. As opções padrão são codificadas como -avze o limite padrão é codificado como 10GiB.

O script

#!/bin/bash

RSYNC=rsync
RSYNC_OPTS=-avz
THRESHOLD=10737418240

usage () {
  echo >&2 "Usage:  $0 from-location to-location"
  exit 1
}

[ "$#" -eq 2 ] || usage

RSYNC_FROM=$1
RSYNC_TO=$2

echo "Fetching file list for $RSYNC $RSYNC_OPTS $RSYNC_FROM $RSYNC_TO"

# get list of changed files
FILES=`$RSYNC $RSYNC_OPTS --dry-run  $RSYNC_FROM $RSYNC_TO | sed -n '/list$/,/^$/{/sending.*list$/ d ; /^$/ d ; /\/$/ d ;; p}'`

# reported files are relative to ..RSYNC_FROM, so rather than transforming filenames, lets just move there
pushd $RSYNC_FROM > /dev/null

# get modified time and sizes for all files
i=0
for FILE in $FILES
do
   #strip first part of path so files are relative to RSYNC_FROM
   FILE=${FILE#*/}
   #FSIZE=`ls -l $FILE | cut -f5 -d' '`
   FSIZE=`du -bs $FILE`
   FMTIME=`ls -l --time-style=+%s $FILE | cut -f6 -d' '`
   FLIST[$i]=`echo $FMTIME $FILE $FSIZE`
   ((i=$i+1))
done

# go back to original directory
popd > /dev/null

# sort list according to modified time
IFS=$'\n' FLIST=($(sort -rg <<<"${FLIST[*]}"))

max=$i
i=0
size=0
#NEWFLIST=''

# add up the files in mtime order until threshold is reached
for ((i=0; i<$max; i++))
do
   s=`echo ${FLIST[$i]} | cut -f3 -d' '`
   f=`echo ${FLIST[$i]} | cut -f2 -d' '`
   ((size=$size+$s))
   if (( "$size" > "$THRESHOLD" ))
   then
      break
   fi
   NEWFLIST="$NEWFLIST $f"
   echo $f >> /tmp/rsyncfilelist
done

$RSYNC $RSYNC_OPTS --dry-run $RSYNC_FROM --files-from=/tmp/rsyncfilelist  $RSYNC_TO

rm /tmp/rsyncfilelist
Casey
fonte
Funciona muito bem, uma vez que não funciona é quando há um arquivo maior que 10 GB como o mais recente arquivo
exussum
Se você sempre deseja que o primeiro arquivo seja transferido independentemente do limite, no loop final dentro do if (( "$size" > "$THRESHOLD" ))condicional, adicione uma verificação (antes break) para i==0e, se for echo $f >> /tmp/rsyncfilelist,.
quer
1

Eu usaria o rsync "--dry-run" (ou "-n") para obter a lista dos arquivos mais recentes. Então eu usaria outro rsync com a opção "--files-from = -" para enviar os arquivos. Entre há perl "feio" .
Algo assim :

#!/usr/bin/perl

$source="/somedir";
$target="host:/remotedir";
$maxsize=10*1024**3; # 10GB 

open (RSOUT,"|rsync -av --files-from=- $source $target");
open (RSIN, "rsync -avn $source $target |");
while (<RSIN>)
{
        chomp;
        last if (/^$/);
        if (-f "$_")
        {
                next if ($size + -s "$_" > $maxsize);
                $size += -s "$_";
                printf RSOUT "%s\n", $_;
        }
}

Note que eu não testei com mais de 10 GB, talvez o perl transborde em algum limite; para resolver isso, em vez de contar bytes, use Kbytes:

$maxsize=10*1024**2; # 10M of Kbytes
...
     $size +=( -s "$_")/1024;

Edição: observei que esta primeira solução não classificaria o arquivo por mtime , aqui está uma solução mais completa (semelhante ao script bash que foi postado por outra pessoa).

#!/usr/bin/perl
use File::stat;

$source="/somedir/";
$target="host:/remotedir";
$maxsize=10 * 1024**3; # 10GB  

open (RSOUT,"|rsync -av --files-from=- $source $target");
open (RSIN, "rsync -avn $source $target |");
while (<RSIN>)
{
    chomp;
    last if (/^$/);
    if (-f "$_")
    {
            my $fileattr;
            my $stat=stat($_);
            $fileattr->{name}=$_;
            $fileattr->{size}=$stat->size;
            $hash{sprintf ("%s %s\n", $stat->mtime, $_)}=$fileattr;
    }

}

foreach $key (reverse sort keys %hash)
{
    next if ( ($size + $hash{$key}->{size}) > $maxsize);
    $size += $hash{$key}->{size};
    print RSOUT $hash{$key}->{name}, "\n";
}
Emmanuel
fonte
0

Você pode analisar a saída classificada de du. Assumindo utilitários GNU:

du -0ak | sort -z -k1n | awk -v 'RS=\0' -v 'ORS=\0' '
    (size += $1) > 10*1024*1024 {quit}
    {print substr($0, index(s, "\t")+1)}
' | xargs -0 cp -t destination

POSIXly, supondo que nenhum nome de arquivo contenha um caractere de nova linha:

du -ak | sort -k1n | awk '
    (size += $1) > 10*1024*1024 {quit}
    {print substr($0, index(s, "\t")+1)}
' | while IFS= read -r filename; do cp -- "$filename" /path/to/destination

Observe que duatravessa subdiretórios. Para evitar isso, diga em duquais arquivos você deseja operar. Em geral, você pode usar findpara filtrar arquivos.

find . -type f ! -name excluded-file -exec du -ak {} + |
sort -k1n | awk '
    (size += $1) > 10*1024*1024 {quit}
    {print substr($0, index(s, "\t")+1)}
' | while IFS= read -r filename; do cp -- "$filename" /path/to/destination
Gilles 'SO- parar de ser mau'
fonte
existe uma maneira de adicionar funções semelhantes ao rsync? isso será executado mais de uma vez, mas esse script copiará os arquivos várias vezes?
exussum 23/10
@ user1281385 Você pode ligar em rsyncvez de cp.
Gilles 'SO- stop be evil'
a função rysnc seria remover os antigos quando executado várias vezes para não não transferir o ficheiro se já existe
exussum