我正在寻找一个命令/脚本来允许将最近修改的文件(最多)10GB 复制到另一台计算机。
因此,如果有 4 个文件,每个文件 4 GB,则脚本只应传输其中的 2 个文件,如果有 12 个文件,每个文件 1 GB 大,则应仅传输最近的 10 个文件。
答案1
这是一个可以满足您要求的脚本。
要求
- 传输的文件总数必须小于阈值大小。
- 与 rsync 目标相比,必须修改这些文件。
- 如果不是所有文件都可以传输,则只能选择最近修改的文件。
细节
它用于rsync --dry-run
构建要传输的文件列表(这些是修改后的文件)。然后,它使用du
和的组合ls
来获取文件大小和运行时间。然后,它按 mtime 对文件进行排序,然后循环遍历它们,直到总大小超过阈值。最后,它再次调用 rsync,仅使用最近修改且总大小低于阈值的文件。
该脚本有点难看,但它有效。一大限制是它必须在包含 rsync from 目录的机器上执行。可以将其修改为使用 ssh 来使用远程目录,但该练习大小留给读者。
最后,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
答案2
我会用同步“--dry-run”(或“-n”)获取较新文件的列表。然后我会用另一个同步使用选项“--files-from=-”发送文件。中间有“丑”珀尔。
像这样的东西:
#!/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", $_;
}
}
注意我没有使用超过 10GB 的数据进行测试,也许 Perl 会在某个限制下溢出;为了解决这个问题,不要使用 Kbytes 来计算字节数:
$maxsize=10*1024**2; # 10M of Kbytes
...
$size +=( -s "$_")/1024;
编辑:我注意到第一个解决方案不会按以下方式对文件进行排序时间,这里有一个更完整的解决方案(类似于另一个人发布的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";
}
答案3
您可以解析 的排序输出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