rsync más reciente x GB


8

Estoy buscando un comando / script para permitir que los archivos modificados más recientemente (hasta) 10GB se copien a otra computadora.

Entonces, si hay 4 archivos de 4 GB cada uno, solo 2 de ellos deberían ser transferidos por la secuencia de comandos. Si hay 12 archivos de 1 GB, solo se deben transferir los 10 más recientes.


1
No se me ocurre ninguna manera de hacer esto, pero para aclarar su pregunta, ¿realmente quiere copiar los últimos 10 GB de archivos modificados, o algún conjunto de archivos de hasta 10 GB? No creo que haya ninguna forma de obligar a rsync a dar prioridad a los archivos más recientes. La respuesta más cercana que se me ocurre sería restringir el ancho de banda a un valor conocido (como 1 MB / segundo) y eliminar rsync después de que haya transcurrido el tiempo suficiente para transferir x GB de datos. No es perfecto ya que la restricción de ancho de banda es un valor máximo, por lo que es posible que no transfiera tanto como desea.
Johnny

el más reciente. por archivo mtime
exussum

Respuestas:


6

Aquí hay un script que hace justo lo que pediste.

Los requisitos

  • Los archivos transferidos deben sumar menos de un umbral.
  • Los archivos deben modificarse en comparación con el destino rsync.
  • Si no se pueden transferir todos los archivos, solo se deben seleccionar los archivos modificados más recientemente.

Los detalles

Se utiliza rsync --dry-runpara crear una lista de archivos que se transferirán (estos son los archivos modificados). Luego usa una combinación de duy lspara obtener tamaños de archivo y mtime. Luego ordena los archivos por mtime y luego los repite hasta que el tamaño total excede un umbral. Finalmente, vuelve a llamar a rsync con solo los archivos modificados más recientemente y con el tamaño total por debajo del umbral.

El guión es un poco feo, pero funciona. Una gran limitación es que debe ejecutarse en la máquina que contiene el directorio rsync. Se puede modificar para usar ssh para usar un directorio remoto desde, pero ese exceso se deja al lector.

Finalmente, las rsyncopciones están codificadas en el script, pero este es un cambio fácil si desea especificarlas en la línea de comandos. Además, la matemática para calcular el tamaño se realiza en bytes. Esto se puede cambiar a kilo / mega / gigabytes modificando la llamada a du y reduciendo el umbral en el mismo factor.

Uso

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

donde rsync-from-directoryes un directorio local y rsync-to-directoryes cualquier directorio local o remoto. Las opciones predeterminadas están codificadas como -avzy el umbral predeterminado está codificado como 10GiB.

La secuencia de comandos

#!/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

Funciona muy bien, una vez que no funciona es cuando hay un archivo de más de 10 GB como el archivo más reciente
exussum

Si siempre desea que el primer archivo se transfiera independientemente del umbral, en el bucle final dentro del if (( "$size" > "$THRESHOLD" ))condicional, agregue una marca (antes break) para i==0y, de ser así echo $f >> /tmp/rsyncfilelist,.
casey

1

Me gustaría utilizar rsync "dry-run" (o "-n") para obtener la lista de los archivos más recientes. Luego usaría otro rsync con la opción "--files-from = -" para enviar los archivos. En el medio hay perl "feo" .
Algo como esto :

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

Tenga en cuenta que no probé con más de 10 GB, tal vez Perl se desbordará en algún límite; para resolver eso, en lugar de contar bytes, use Kbytes:

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

EDITAR: Noté que esta primera solución no ordenaría el archivo por mtime , aquí hay una solución más completa (similar al script bash que ha sido publicado por otra persona).

#!/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

Puede analizar la salida ordenada de du. Suponiendo utilidades 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

POSIXY, suponiendo que ningún nombre de archivo contenga un carácter de nueva línea:

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

Tenga en cuenta que duatraviesa subdirectorios. Para evitar eso, indique en duqué archivos desea operar. De manera más general, puede usar findpara filtrar archivos.

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

¿hay alguna manera de agregar rsync como funciones? Esto se ejecutará más de una vez, pero este script copiará los archivos varias veces.
exussum

@ user1281385 Puede llamar en rsynclugar de cp.
Gilles 'SO- deja de ser malvado'

la función rysnc sería eliminar las antiguas cuando se ejecutan varias veces para no transferir el archivo si ya existe
exussum
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.