rsync le plus récent x Go

8

Je recherche une commande / un script permettant de copier les derniers fichiers modifiés (jusqu'à) 10 Go sur un autre ordinateur.

Donc, s'il y a 4 fichiers de 4 Go chacun, seuls 2 d'entre eux doivent être transférés par le script.S'il y a 12 fichiers de 1 Go, seuls les 10 plus récents doivent être transférés.

exussum
la source
1
Je ne vois pas de moyen de le faire, mais pour clarifier votre question, voulez-vous vraiment que les 10 Go de fichiers les plus récemment modifiés soient copiés, ou tout ensemble de fichiers jusqu'à 10 Go? Je ne pense pas qu'il existe un moyen de forcer rsync à donner la priorité aux fichiers les plus récents. La réponse la plus proche à laquelle je peux penser serait de limiter la bande passante à une valeur connue (comme 1 Mo / seconde) et de tuer rsync après un délai suffisant pour transférer x Go de données. Pas parfait car la contrainte de bande passante est une valeur maximale, vous ne pouvez donc pas transférer autant que vous le souhaitez.
Johnny
la plus récente. par fichier
mtime

Réponses:

6

Voici un script qui fait exactement ce que vous avez demandé.

Les exigences

  • Les fichiers transférés doivent totaliser moins d'une taille seuil.
  • Les fichiers doivent être modifiés par rapport à la destination rsync.
  • Si tous les fichiers ne peuvent pas être transférés, seuls les fichiers modifiés les plus récents doivent être sélectionnés.

Les détails

Il utilise rsync --dry-runpour construire une liste de fichiers qui seraient transférés (ce sont les fichiers modifiés). Il utilise ensuite une combinaison de duet lspour obtenir les tailles de fichier et mtime. Il trie ensuite les fichiers par mtime, puis les boucle jusqu'à ce que la taille totale dépasse un seuil. Enfin, il appelle à nouveau rsync avec uniquement les fichiers dont la taille totale a été modifiée le plus récemment et sous le seuil.

Le script est un peu moche, mais ça marche. Une grande limitation est qu'il doit être exécuté sur la machine contenant le répertoire rsync. Il peut être modifié pour utiliser ssh pour utiliser un répertoire distant, mais cette excersize est laissée au lecteur.

Enfin, les rsyncoptions sont codées en dur dans le script, mais c'est un changement facile si vous souhaitez les spécifier sur la ligne de commande. De plus, les calculs pour calculer la taille se font en octets. Cela peut être changé en kilo / méga / gigaoctets en modifiant l'appel à du et en réduisant le seuil du même facteur.

Usage

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

rsync-from-directoryest un répertoire local et rsync-to-directoryn'importe quel répertoire local ou distant. Les options par défaut sont codées en dur comme -avzet le seuil par défaut est codé en dur comme 10GiB.

Le scénario

#!/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
la source
Fonctionne très bien, Une fois que cela ne fonctionne pas, c'est quand il y a un fichier de plus de 10 Go comme fichier le plus récent
Exussum
Si vous souhaitez toujours que le premier fichier soit transféré quel que soit le seuil, dans la boucle finale à l'intérieur du if (( "$size" > "$THRESHOLD" ))conditionnel, ajoutez une vérification (avant break) i==0et si oui echo $f >> /tmp/rsyncfilelist,.
casey
1

J'utiliserais rsync "--dry-run" (ou "-n") pour obtenir la liste des fichiers les plus récents. Ensuite, j'utiliserais un autre rsync avec l'option "--files-from = -" pour envoyer les fichiers. Entre les deux, il y a du "laid" perl .
Quelque chose comme ça :

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

Notez que je n'ai pas testé avec plus de 10 Go, peut-être que perl débordera à une certaine limite; pour résoudre cela, au lieu de compter les octets, utilisez Kbytes:

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

EDIT: J'ai remarqué que cette première solution ne trierait pas les fichiers par mtime , voici une solution plus complète (similaire au script bash qui a été posté par une autre personne).

#!/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
la source
0

Vous pouvez analyser la sortie triée de du. En supposant que les utilitaires 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

POSIX, en supposant qu'aucun nom de fichier ne contient un caractère de nouvelle ligne:

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

Notez que dutraverse les sous-répertoires. Pour éviter cela, indiquez sur duquels fichiers vous souhaitez opérer. Plus généralement, vous pouvez utiliser findpour filtrer les fichiers.

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- arrête d'être méchant'
la source
existe-t-il un moyen d'ajouter des fonctions de type rsync? cela sera exécuté plus d'une fois mais ce script copiera les fichiers plusieurs fois?
exussum
@ user1281385 Vous pouvez appeler rsyncau lieu de cp.
Gilles 'SO- arrête d'être méchant'
la fonction rysnc serait de supprimer les anciens lors de plusieurs exécutions pour ne pas transférer le fichier s'il existe déjà
exussum