如何在Perl中归档一个目录

4

我正在尝试归档一个包含所有子目录和文件的目录。但是以下代码不能正常工作。它只会归档文件和文件夹,而不是文件夹中的内容!我该如何解决这个问题?

my @files = glob( $BACKUP_PATH.'website/*' );
my $tar = Archive::Tar->new();
$tar->add_files(@files);

my $date = DateTime->now(time_zone=>"local");
$tar->write($BACKUP_PATH.$websiteFolderName."/".$date->dmy('-')."-".$websiteFolderName.".tar");

我认为这不是重复的问题。另一个问题是在询问如何使用Archive::Tar。而这个问题已经有了答案,但是OP的实现逻辑存在问题。 - simbabque
1
你有检查过@files包含的内容吗? - simbabque
你在调试器中执行过这个程序吗?那是开始的地方。 - Jim Garrison
2个回答

3
我想写一个高级版的翻译,因为我对已接受的解决方案感到非常失望。我想向您展示可能性和我认为是正确的方式。
我做了这些改变。
  • 我正在使用Time::Piece来获取文件名的日期戳。这是一个核心模块,生成%d-%m-%Y日期戳所需的一切。非核心的DateTime模块对于如此简单的目的而言需要加载大量代码。

    顺便说一下,我使用了ymd格式而不是dmy。如果您想按照自己编写代码的方式进行更改,那么可以将其改回去,但是ymd名称很有用,因为它们按日期顺序排序。

  • 我使用了File::Spec::Functions中的catdircatfile,它们是核心File::Spec库的过程接口。这样可以避免担心一个变量在末尾有斜杠,另一个在开头有斜杠,并且使代码更清晰,因为它避免了所有那些令人讨厌的字符串连接。

  • 我使用了File::Path中的make_path来确保目标目录实际存在。

  • 由于要归档的文件路径已经存储在数组中,因此没有理由不使用create_archive类方法一次性构建和保存您的文件。

    create_archive的第二个参数是压缩方法。我将其设置为零以请求无压缩和最大速度,但是您可以通过传递COMPRESS_GZIPCOMPRESS_BZIP启用gzip或bzip压缩。

    虽然该模块的文档不鼓励这样做,但您也可以将此参数设置为1到9之间的值,以指示gzip压缩级别。如果您正在归档文本文件,则强烈建议在此处使用1或2。传递COMPRESS_GZIP会导致gzip压缩级别为9,这是远远最慢的,并且通常会导致*比较低的压缩更大的文件。值为1通常会给您一个未压缩存档大小的三分之一的文件,而2可能会给您另外5%的减少。之后通常很少有收益。

  • 我添加了一些日志消息以增加程序的可信度并通知其进展情况。我还编写了一个小的bytes子例程,将文件大小转换为适当的1KB幂次方,以使其更易读。

我使用Windows路径是因为我正在使用这个桌子。由于使用了File::Spec,一旦您在代码顶部设置好路径和文件名,它也应该可以在Linux上正常工作。

无论您是否使用此代码,我都希望您找到它有用。

use strict;
use warnings;

use Archive::Tar;
use File::Find qw/ find /;
use Time::Piece;
use File::Spec::Functions qw/ catfile catdir /;
use File::Path qw/ make_path /;

STDOUT->autoflush;

my $BACKUP_PATH         = 'E:/Perl';
my $website_folder_name = 'website';
my $website_backup_name = 'website_backup';

print "Making list of files to archive\n";

my $source_dir = catdir($BACKUP_PATH, $website_folder_name);
my @filelist;

find( sub {
    return unless -f;
    push @filelist, $File::Find::name;
}, $source_dir);

printf "List complete - %d files to be archived\n", scalar @filelist;

my $tardir = catdir($BACKUP_PATH, $website_backup_name);
make_path($tardir, { verbose => 1 } );

my $date = localtime()->ymd;
my $tarfile = catfile($tardir, "$date-$website_backup_name.tar");
print qq{Writing archive to "$tarfile"\n};

my $success = Archive::Tar->create_archive($tarfile, 0, @filelist);
printf "Archive written successfully - %s\n", bytes(-s $tarfile) if $success;


sub bytes {
    my ($size) = @_;

    my @prefix = ( '', qw/ K M G T P E / );

    my $pow = 0;
    while ( $size >= 1024 ) {
        $size /= 1024;
        ++$pow;
    }

    sprintf "%.0f%sB", $size, $prefix[$pow];
}

输出

Making list of files to archive
List complete - 6602 files to be archived
Writing archive to "E:\Perl\website_backup\2016-01-07-website_backup.tar"
Archive written successfully - 7MB

2

glob (perldoc) 不会递归进入子目录。您需要自己处理或使用一些模块,例如File::Find

use Archive::Tar;
use File::Find qw(find);

my @files;
find( { wanted => sub { push @files, $File::Find::name } }, $BACKUP_PATH.'website');
my $tar = Archive::Tar->new();
$tar->add_files( @files );

my $date = DateTime->now(time_zone=>"local");
$tar->write($BACKUP_PATH.$websiteFolderName."/".$date->dmy('-')."-".$websiteFolderName.".tar");

1
它不起作用。它抛出一个错误:在test.pl的第24行,无法找到文件或目录:该行 --> find({wanted... - hkaraoglu
1
或者更好的选择是:File::Find::Rule - Axeman
2
不是我的否定意见,但你应该: 省略 'use strict' 和'use warnings' all' ;省略'use DateTime';不要声明或定义"$BACKUP_PATH"或"$websiteFolderName";使用字符串连接构建文件路径;不要测试你的解决方案。 - Borodin
@zb226 不是我。我只是想添加一个链接到FFR。FF仍然是一种有效的方法,而且据我所知,它是目前最高效的方法。 - Axeman
@Axeman: 我从未在生产代码中看到过它,也希望不会看到。你看过源代码吗?它真的很恶心,只是为了启动自己而挖掘 Perl 的内部。要小心。 - Borodin
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接