如何比较 tar 存档和目录中的文件列表?

Posted

技术标签:

【中文标题】如何比较 tar 存档和目录中的文件列表?【英文标题】:How can I compare file list from a tar archive and directory? 【发布时间】:2009-08-13 13:14:37 【问题描述】:

我还在学习 Perl。谁能建议我使用 Perl 代码来比较 .tar.gz 和目录路径中的文件。

假设我有几天前使用的以下目录路径的 tar.gz 备份。

a/file1
a/file2
a/file3
a/b/file4
a/b/file5
a/c/file5
a/b/d/file and so on..

现在我想将该路径下的文件和目录与 tar.gz 备份文件进行比较。

请建议使用 Perl 代码。

【问题讨论】:

【参考方案1】:

见Archive::Tar。

【讨论】:

【参考方案2】:

Archive::TarFile::Find 模块会很有帮助。下面显示了一个基本示例。它只是打印有关 tar 中的文件和目录树中的文件的信息。

从您的问题中不清楚您想如何比较文件。如果需要比较实际内容,可能需要Archive::Tar::File 中的get_content() 方法。如果更简单的比较就足够了(例如,名称、大小和 mtime),那么您只需要以下示例中使用的方法即可。

#!/usr/bin/perl
use strict;
use warnings;

# A utility function to display our results.
sub Print_file_info 
    print map("$_\n", @_), "\n";


# Print some basic information about files in a tar.
use Archive::Tar qw();
my $tar_file = 'some_tar_file.tar.gz';
my $tar = Archive::Tar->new($tar_file);
for my $ft ( $tar->get_files )
    # The variable $ft is an Archive::Tar::File object.
    Print_file_info(
        $ft->name,
        $ft->is_file ? 'file' : 'other',
        $ft->size,
        $ft->mtime,
    );


# Print some basic information about files in a directory tree.
use File::Find;
my $dir_name = 'some_directory';
my @files;
find(sub push @files, $File::Find::name, $dir_name);
Print_file_info(
    $_,
    -f $_ ? 'file' : 'other',
    -s,
    (stat)[9],
) for @files;

【讨论】:

@FM AFAIK, Archive::Tar->new 需要被告知文件已压缩。 @Sinan Unur。好点子;这也是我阅读文档的方式。但是,我刚刚在上面的脚本中测试了$ft->get_content,它返回了正确的内容,即使没有添加压缩标志(在 Windows 框中)。在这一点上,我不确定一种或另一种方式……听起来对 SO 来说是个好问题。 @FM 啊哈!查看源代码,似乎$compressed 标志用于Arcive::Tar 的输出,而内部_get_handle 检测文件是否被压缩。 @Sinan Unur。很高兴知道。谢谢。【参考方案3】:

Perl 在这方面有点矫枉过正,真的。一个shell脚本就可以了。您需要采取的步骤:

将 tar 解压到某个临时文件夹中。 diff -uR 两个文件夹并将输出重定向到某处(或者可能通过管道传输到 less 视情况而定) 清理临时文件夹。

你就完成了。不应超过 5-6 行。快速且未经测试的东西:

#!/bin/sh
mkdir $TEMP/$$
tar -xz -f ../backups/backup.tgz $TEMP/$$
diff -uR $TEMP/$$ ./ | less
rm -rf $TEMP/$$

【讨论】:

我不想创建任何文件夹。没有任何方法可以从 .tar.gz 读取文件并放入哈希并进行比较。 为什么使用 diff 提取和比较.. 为什么不压缩然后使用 zdiff 进行比较.. 虽然我不确定 zdiff 是如何工作的,但它应该占用更少的空间,但只是好奇 :) 我认为 zdiff 仅适用于文件,但我在 .tar.gz 文件下有目录。 “我不想创建任何文件夹。” 不想,还是不能?做你所描述的事情相对困难,当然不是微不足道的,而且超出了任何人愿意在这里为你做的任何事情。 要在不解压缩您想要使用Archive::Tar 的压缩包的情况下执行此操作,请遍历存档的每个成员,然后以取决于类型的方式将其与磁盘上的现有文件进行比较文件(比较常规文件的内容和可能的时间,readlink 用于符号链接,查看stat 信息以获取设备特价等)对于初学者来说,这不是一个理想的任务。哦,Archive::Tar 不知道如何从磁盘流式传输文件;它将所有数据加载到内存中。我认为低技术差异解决方案获胜。您可以通过将/tmp 放在 tmpfs 上来帮助自己。【参考方案4】:

这是一个检查存档中的每个文件是否也存在于文件夹中的示例。

# $1 is the file to test
# $2 is the base folder
for file in $( tar --list -f $1 | perl -pe'chomp;$_=qq["'$2'$_" ]' )
do
  # work around bash deficiency
  if [[ -e "$( perl -eprint$file )" ]]
    then
      echo "   $file"
    else
      echo "no $file"
  fi
done

我是这样测试的:

我删除/重命名config,然后运行以下命令:

bash 测试 下载/update-dnsomatic-0.1.2.tar.gz 下载/

输出如下:

“下载/更新-dnsomatic-0.1.2/” 没有“下载/更新-dnsomatic-0.1.2/config” “下载/update-dnsomatic-0.1.2/update-dnsomatic” “下载/更新-dnsomatic-0.1.2/README” “下载/更新-dnsomatic-0.1.2/install.sh”

我是 bash / shell 编程的新手,所以可能有更好的方法来做到这一点。

【讨论】:

【参考方案5】:

对于一个好的 Perl 程序来说,这可能是一个很好的起点。不过,它可以满足问题的要求。

它只是被破解在一起,忽略了大多数 Perl 的最佳实践。

perl test.pl 完整的\ 下载/更新-dnsomatic-0.1.2.tar.gz \ 下载/\ 更新-dnsomatic-0.1.2
#! /usr/bin/env perl
use strict;
use 5.010;
use warnings;
use autodie;

use Archive::Tar;
use File::Spec::Functions qw'catfile catdir';

my($action,$file,$directory,$special_dir) = @ARGV;

if( @ARGV == 1 )
  $file = *STDOUTIO;

if( @ARGV == 3 )
  $special_dir = '';


sub has_file(_);
sub same_size($$);
sub find_missing(\%$);

given( lc $action )

  # only compare names
  when( @[qw'simple name names'] )
    my @list = Archive::Tar->list_archive($file);

    say qq'missing file: "$_"' for grep ! has_file  @list;
  

  # compare names, sizes, contents
  when( @[qw'full aggressive'] )
    my $next = Archive::Tar->iter($file);
    my( %visited );

    while( my $file = $next->() )
      next unless $file->is_file;
      my $name = $file->name;
      $visited$name = 1;

      unless( has_file($name) )
        say qq'missing file: "$name"' ;
        next;
      

      unless( same_size( $name, $file->size ) )
        say qq'different size: "$name"';
        next;
      

      next unless $file->size;

      unless( same_checksum( $name, $file->get_content ) )
        say qq'different checksums: "$name"';
        next;
      
    

    say qq'file not in archive: "$_"' for find_missing %visited, $special_dir;
  



sub has_file(_)
  my($file) = @_;
  if( -e catfile $directory, $file )
    return 1;
  
  return;


sub same_size($$)
  my($file,$size) = @_;
  if( -s catfile($directory,$file) == $size )
    return $size || '0 but true';
  
  return; # empty list/undefined


sub same_checksum
  my($file,$contents) = @_;
  require Digest::SHA1;

  my($outside,$inside);

  my $sha1 = Digest::SHA1->new;
  
    open my $io, '<', catfile $directory, $file;
    $sha1->addfile($io);
    close $io;
    $outside = $sha1->digest;
  

  $sha1->add($contents);
  $inside = $sha1->digest;


  return 1 if $inside eq $outside;
  return;


sub find_missing(\%$)
  my($found,$current_dir) = @_;

  my(@dirs,@files);

  
    my $open_dir = catdir($directory,$current_dir);
    opendir my($h), $open_dir;

    while( my $elem = readdir $h )
      next if $elem =~ /^[.]1,2[\\\/]?$/;

      my $path = catfile $current_dir, $elem;
      my $open_path = catfile $open_dir, $elem;

      given($open_path)
        when( -d )
          push @dirs, $path;
        
        when( -f )
          push @files, $path, unless $found->$path;
        
        default
          die qq'not a file or a directory: "$path"';
        
      
    
  

  for my $path ( @dirs )
    push @files, find_missing %$found, $path;
  

  return @files;

config 重命名为config.rm 后,在README 中添加一个额外的字符,在install.sh 中更改一个字符,并添加一个文件.test。这是它的输出:

缺少文件:“update-dnsomatic-0.1.2/config” 不同大小:“update-dnsomatic-0.1.2/README” 不同的校验和:“update-dnsomatic-0.1.2/install.sh” 文件不在存档中:“update-dnsomatic-0.1.2/config.rm” 文件不在存档中:“update-dnsomatic-0.1.2/.test”

【讨论】:

以上是关于如何比较 tar 存档和目录中的文件列表?的主要内容,如果未能解决你的问题,请参考以下文章

如何获取存档中的文件列表? [复制]

使用 SharpZipLib 制作没有文件夹结构的 .tar.gz 存档

提取存储在 tar 存档中的文件的先前版本

如何只查看tar.gz压缩文件中顶层目录的列表

tar命令是怎么使用?

列出和删除存档中的目录