Frage

Ich lerne noch Perl. Kann jemand mir bitte den Perl-Code vorschlägt Dateien aus tar.gz und einem Verzeichnispfad zu vergleichen.

Lassen Sie uns sagen, ich habe tar.gz Backup-Verzeichnispfad zu folgen, die ich einige Tage zurückgenommen haben.

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

Jetzt möchte ich Dateien und Verzeichnisse mit der tar.gz Sicherungsdatei unter diesem Pfad vergleichen.

Bitte legen nahe, Perl-Code zu tun.

War es hilfreich?

Lösung

Dies könnte ein guter Ausgangspunkt für ein gutes Perl-Programm sein. Es tut, was die Frage nach obwohl gefragt.

Es war einfach zusammen gehackt, und ignoriert die meisten der besten Praktiken für Perl.

perl test.pl full                            \
     Downloads/update-dnsomatic-0.1.2.tar.gz \
     Downloads/                              \
     update-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 = *STDOUT{IO};
}
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;
}

Nach config zu config.rm umbenennen, ein zusätzliches Zeichen Hinzufügen eines char in README install.sh, Ändern und Hinzufügen einer Datei .test. Dies ist, was es ausgegeben:

missing file: "update-dnsomatic-0.1.2/config"
different size: "update-dnsomatic-0.1.2/README"
different checksums: "update-dnsomatic-0.1.2/install.sh"
file not in archive: "update-dnsomatic-0.1.2/config.rm"
file not in archive: "update-dnsomatic-0.1.2/.test"

Andere Tipps

Die Archive::Tar und File::Find Module wird hilfreich sein. Ein einfaches Beispiel ist unten gezeigt. Er druckt nur Informationen über die Dateien in einem tar und die Dateien in einem Verzeichnisbaum.

Es war nicht klar, aus Ihrer Frage, wie Sie die Dateien vergleichen möchten. Wenn Sie den tatsächlichen Inhalt, die get_content() Methode in Archive::Tar::File vergleichen, müssen wahrscheinlich erforderlich sein. Wenn ein einfacher Vergleich angemessen ist (beispielsweise Name, Größe und mtime), werden Sie nicht brauchen viel mehr als Methoden im Beispiel unten.

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

Perl ist eine Art Overkill für diese, wirklich. Ein Shell-Skript würde gut tun. Die Schritte müssen Sie aber nehmen:

  • Extrahieren Sie die tar in einen temporären Ordner irgendwo.
  • diff -uR die beiden Ordner und leiten Sie die Ausgabe irgendwo (oder vielleicht Rohr gegebenenfalls less)
  • Reinigen Sie den temporären Ordner auf.

Und du bist fertig. Sollte nicht mehr als 5-6 Zeilen sein. Etwas schnell und ungetestet:

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

Heres ein Beispiel, das, wenn jede Datei überprüft, die in einem Archiv ist, existiert auch in einem Ordner.

# $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

Dies ist, wie ich diese getestet:

I entfernt / umbenannten config, lief dann wie folgt vor:

bash test Downloads/update-dnsomatic-0.1.2.tar.gz Downloads/

, der die Ausgabe von haben:

   "Downloads/update-dnsomatic-0.1.2/"
no "Downloads/update-dnsomatic-0.1.2/config"
   "Downloads/update-dnsomatic-0.1.2/update-dnsomatic"
   "Downloads/update-dnsomatic-0.1.2/README"
   "Downloads/update-dnsomatic-0.1.2/install.sh"

Ich bin neu in bash / Shell-Programmierung, so gibt es wahrscheinlich einen besseren Weg, dies zu tun.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top