rsync x GB mais recente

8

Estou procurando um comando / script para permitir que os arquivos modificados mais recentemente (até) 10GB 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, somente os 10 mais recentes deverão ser transferidos.

    
por exussum 23.10.2013 / 02:03

3 respostas

6

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

Os requisitos

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

Os detalhes

Ele usa rsync --dry-run para criar uma lista de arquivos que seriam transferidos (esses são os arquivos modificados). Em seguida, ele usa uma combinação de du e ls para obter tamanhos de arquivo e mtime. Em seguida, ele classifica os arquivos por mtime e, em seguida, faz um loop sobre eles até que o tamanho total exceda um limite. Finalmente, ele chama novamente o rsync apenas com os arquivos que foram modificados recentemente e o 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 o ssh para usar um diretório remoto, mas esse excerso é deixado para o leitor.

Por fim, as opções rsync 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 kilo / mega / gigabytes, modificando a chamada para du e reduzindo o limite pelo mesmo fator.

Uso

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

em que rsync-from-directory é um diretório local e rsync-to-directory é qualquer diretório local ou remoto. As opções padrão são codificadas como -avz e 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
    
por 04.12.2013 / 19:37
1

Eu usaria rsync "--dry-run" (ou "-n") para obter a lista dos arquivos mais novos. Então eu usaria outro rsync com a opção "--files-from = -" para enviar os arquivos. Entre há "feio" perl .
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 10GB, talvez o perl transbordará em algum limite; para resolver isso, em vez de contar bytes, use Kbytes:

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

EDITAR: Notei que esta primeira solução não ordenaria 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";
}
    
por 04.12.2013 / 18:24
0

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

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

Observe que du atravessa subdiretórios. Para evitar isso, informe du de quais arquivos você deseja operar. Mais geralmente, você pode usar find para filtrar arquivos.

%pre%     
por 23.10.2013 / 18:16