对于图像和分区,比rsync更快的perl脚本会产生双向差异

时间:2014-03-27 16:37:08

标签: perl bash diff binaryfiles disk-partitioning

sysadmin1138Martinreportedreplacement for rsync,适用于块设备(分区)。它基于perl,但我想存储双向差异。

它将块设备中的更改应用于预先存在的过期备份映像。这是第二次做到这一点,在lvmsync后我没有使用,因为我的块设备不在lvm中。

但我还想单独收集更改,以便能够重新生成以前的备份映像(例如,恢复已删除的文件)。

当rsync重新进行运行时,以下代码会收集这些更改:

patch=diff.`date +'%Y%m%d.%H%M%S.%N'`.gz
ssh $username@$backupnas "perl -'MDigest::MD5 md5' -ne   "\
"        'BEGIN{\$/=\1024};print md5(\$_)' $remotepartition        "\
" | gzip -c                                              "\
|gunzip -c|LANG= tee >(wc -c|LANG= sed '1s%^%number of 64 bytes blocs: %' >&2) \
|LANG= perl -'MDigest::MD5 md5' -e 'open DISK,"'"<$partition"'" or die $!; '\
'         while( read DISK,$read,1024)                                     '\
'         {                                                                '\
'           read STDIN,$md,16;                                             '\
'           if($md eq md5($read)) {print "s"} else {print "c" . $read }    '\
'         }                                                                '\
| gzip -c                                                                       \
|ssh $username@$backupnas "touch $remotepartition;LANG= tee -a $patch|gunzip -c"\
"     |perl -e 'open REVP,\"| gzip -c > rev.$patch\";                          "\
"         open PREVIOUS,\"<$remotepartition\";                                 "\
'         $rev = "PREVIOUS met EOF if length<1024."; $rev=$rev.$rev;           '\
'         $rev=$rev.$rev.$rev.$rev; $rev=$rev.$rev.$rev.$rev;                  '\
'         while(read STDIN,$read,1)                                            '\
'         {                                                                    '\
'           if ($read eq "s")                                                  '\
'           {                                                                  '\
'             if (length($rev) eq 1024) { print REVP "s" } ;                   '\
'             $s++                                                             '\
'           } else {                                                           '\
'             if ($s) { seek STDOUT,$s*1024,1; seek PREVIOUS,$s*1024,1; $s=0}; '\
'             if (read PREVIOUS,$rev,1024) { print REVP "c".$rev };            '\
'             read STDIN,$buf,1024;                                            '\
'             print $buf                                                       '\
'           }                                                                  '\
"         }' 1<> $remotepartition                                              "

$rev被初始化为长度为1024的标量字符串(我不知道如何让它变得更好)。

没有格式化和更多or die,这是:

patch=essai_delta.`date +'%Y%m%d.%H%M%S.%N'`.gz
ssh username@backupnas "perl -'MDigest::MD5 md5' -ne 'BEGIN{\$/=\1024};print md5(\$_)' essai_backup | gzip -c" | \
gunzip -c | LANG= tee >(wc -c|LANG= sed '1s%^%bin/backup_essai: number of 64 bytes blocs treated : %' >&2) | \
LANG= perl -'MDigest::MD5 md5' -e 'open DISK,"</data/data/com.spartacusrex.spartacuside/files/essai" or die $!; while( read DISK,$read,1024) { read STDIN,$md,16; if($md eq md5($read)) {print "s"} else {print "c" . $read } }' /data/data/com.spartacusrex.spartacuside/files/essai | \
gzip -c | \
ssh username@backupnas "LANG= tee -a $patch | gunzip -c | perl -e 'open REVP,\"| gzip -c > rev.$patch\" or die \$!; open READ,\"<essai_backup\" or die \$!; \$rev = \"if length<1024, EOF met in READ.\"; \$rev=\$rev.\$rev.\$rev.\$rev; \$rev=\$rev.\$rev.\$rev.\$rev; \$rev=\$rev.\$rev; while(read STDIN,\$read,1) { if (\$read eq \"s\") {if (length(\$rev) eq 1024) { print REVP \"s\" or die \$! } ; \$s++} else { if (\$s) { seek STDOUT,\$s*1024,1 or die \$!; seek READ,\$s*1024,1 or die \$!; \$s=0}; if (read READ,\$rev,1024) { print REVP \"c\".\$rev or die \$! } else { print STDERR \$!}; read STDIN,\$buf,1024 or die \$!; print \$buf  or die \$!} }' 1<> essai_backup"

要应用前向或后向差异,我可以使用:

ssh username@backup_nas "LANG= cat diff_delta.20141202.110302.0935 | gunzip -c | perl -ne 'BEGIN{\$/=\1} if (\$_ eq\"s\") {\$s++} else {if (\$s) { seek STDOUT,\$s*1024,1; \$s=0}; read STDIN,\$buf,1024; print \$buf}' 1<> image.file"

所以我成功回答了这篇文章的第一个版本。这是在一个200k的例子上进行了测试,并进行了一些修改。

我对此代码有具体问题。

为什么original example使用了read ARGV,这是不好的做法?

我放了很多or die $!,这是明智的,还是只是破坏了可读性?

PREVIOUSSTDOUT是同一个打开两次的文件(为了避免seek STDOUT,-1024,1),这被视为良好做法吗?

[从programmers.so手动迁移的问题]

1 个答案:

答案 0 :(得分:2)

  

为什么原来的例子使用了ARGV,这是不好的做法吗?

这是一个宗教问题。对于像这样的单线SSH黑客,如果你和那些可能维护它们的人非常擅长于perl习语,那么它或多或少就好了。但常见的是,新的perl代码应该use strict;并使用更直观地阅读的约定。你不得不询问裸ARGV并被提到一篇不起眼的perlmonk文章的事实正是为什么。我希望有机会将编写良好的可读脚本分发到目标计算机上的标准位置,然后使用 simple ssh命令远程运行它们。另一方面,上述方式对于工作保障很有帮助。

  

我已经放了很多或死了$!,这是明智的还是只是破坏了可读性?

知道脚本死亡的原因而不是获得模糊的默认错误跟踪总是很方便。可读性问题只是您使用这种在ssh命令中放入相当大的脚本的破坏技术。如上所述,如果您为自己设置了更好的环境,添加or die $!将不会损害可读性。它会通过显示您可能出现错误的位置来增强它。

  

PREVIOUS和STDOUT是同一个文件打开两次(避免寻找STDOUT,-1024,1),这是不错的做法?

如果操作系统允许,那么在同一个线程中的同一个文件上打开两个描述符也不错。它有点模糊,所以需要评论。如果您避免使用内嵌脚本,这是另一件事。

真正奇怪的做法是将$rev的缓冲区通过重复串联构建为字符串,以获得1024个字符。这是不必要的。您可以说$rev = '';,字符串的长度将自动扩展为read的输入大小。如果你真的想要预先分配,只需说出$rev = '-' x 1024;

<强>加成

我刚刚了解了bash的一个很好的功能。其printf %q格式说明符会将bash转义符添加到任何字符串中。有了这个,您可以编写无逃逸的bash和/或perl代码然后说

ssh $username@$backupnas "$(printf "%q" $(cat script.bash))"