rsync самый последний х ГБ

8

Я ищу команду / скрипт, позволяющий копировать последние измененные файлы (до 10 ГБ) на другой компьютер.

Таким образом, если имеется 4 файла по 4 ГБ каждый, только 2 из них должны быть переданы сценарием. Если существует 12 файлов размером 1 ГБ, должны быть переданы только самые последние 10 файлов.

exussum
источник
1
Я не могу придумать какой-либо способ сделать это, но, чтобы прояснить ваш вопрос, действительно ли вы хотите скопировать самые последние 10 ГБ файлов или какой-нибудь набор до 10 ГБ файлов? Я не верю, что есть способ заставить rsync отдавать приоритет самым последним файлам. Самый близкий ответ, который я могу придумать, - ограничить полосу пропускания известным значением (например, 1 МБ / с) и завершить работу rsync по истечении достаточного времени для передачи x ГБ данных. Не идеально, так как ограничение полосы пропускания является максимальным значением, поэтому вы можете передавать не так много, как хотели.
Джонни
Самый последний. по файлу mtime
exussum

Ответы:

6

Вот скрипт, который делает именно то, что вы просили.

Требования

  • Передаваемые файлы должны иметь размер меньше порогового.
  • Файлы должны быть изменены по сравнению с назначением rsync.
  • Если не все файлы могут быть переданы, должны быть выбраны только самые последние измененные файлы.

Детали

Он использует rsync --dry-runдля создания списка файлов, которые будут переданы (это измененные файлы). Затем он использует комбинацию duи, lsчтобы получить размеры файлов и mtime. Затем он сортирует файлы по mtime, а затем перебирает их, пока общий размер не превысит пороговое значение. Наконец, он снова вызывает rsync только с файлами, которые были недавно изменены и имеют общий размер ниже порогового значения.

Сценарий немного уродлив, но работает. Одно большое ограничение заключается в том, что он должен выполняться на машине, содержащей директорию rsync from. Его можно изменить, чтобы использовать ssh для использования удаленного директории from, но этот размер оставляется читателю.

Наконец, rsyncпараметры жестко запрограммированы в скрипте, но это легко изменить, если вы хотите указать их в командной строке. Также математика для вычисления размера выполняется в байтах. Это можно изменить на килограмм / мега / гигабайт, изменив вызов du и уменьшив порог на тот же коэффициент.

Применение

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

где rsync-from-directoryэто локальный каталог и rsync-to-directoryлюбой локальный или удаленный каталог. Параметры по умолчанию жестко заданы как, -avzа пороговое значение по умолчанию жестко задано как 10GiB.

Сценарий

#!/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
источник
Прекрасно работает, один раз это не работает, когда есть файл размером более 10 ГБ в качестве самого последнего файла
exussum
Если вы всегда хотите, чтобы первый файл передавался независимо от порогового значения, в заключительном цикле внутри if (( "$size" > "$THRESHOLD" ))условного выражения добавьте проверку (перед break) для i==0и, если это так echo $f >> /tmp/rsyncfilelist,.
Кейси
1

Я бы использовал rsync «--dry-run» (или «-n»), чтобы получить список более новых файлов. Тогда я бы использовал другой rsync с опцией "--files-from = -" для отправки файлов. Между ними есть "ужасный" perl .
Что-то вроде этого :

#!/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", $_;
        }
}

Обратите внимание, что я не тестировал с более чем 10 ГБ, возможно, Perl переполнится при некотором ограничении; чтобы решить это, вместо подсчета байтов используйте Кбайт:

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

РЕДАКТИРОВАТЬ: я отметил, что это первое решение не будет сортировать файл по mtime , вот более полное решение (похоже на сценарий bash, который был опубликован другим человеком).

#!/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";
}
Эммануэль
источник
0

Вы можете проанализировать отсортированный вывод du. Предполагая, что утилиты 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, при условии, что ни одно имя файла не содержит символа новой строки:

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

Обратите внимание, что duпроходит подкаталоги. Чтобы избежать этого, скажите, с duкакими файлами вы хотите работать. В целом, вы можете использовать findдля фильтрации файлов.

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
Жиль "ТАК - перестань быть злым"
источник
Есть ли способ добавить rsync как функции? это будет выполняться несколько раз, но этот сценарий будет копировать файлы несколько раз?
Exussum
@ user1281385 Вы ​​можете позвонить rsyncвместо cp.
Жиль "ТАК - перестать быть злым"
функция rysnc будет состоять в том, чтобы удалять старые при запуске несколько раз, чтобы не пересылать файл, если он уже существует
exussum