Comment puis-je comparer la liste des fichiers à partir d'une archive tar et le répertoire?
Question
Je suis Perl continue à apprendre. Quelqu'un peut-il s'il vous plaît me suggérer le code Perl pour comparer les fichiers de tar.gz et un chemin de répertoire.
Disons que je sauvegarde tar.gz de chemin de répertoire suivant que j'ai pris il y a quelques jours.
a/file1
a/file2
a/file3
a/b/file4
a/b/file5
a/c/file5
a/b/d/file and so on..
Maintenant, je veux comparer les fichiers et répertoires sous ce chemin avec le fichier de sauvegarde tar.gz.
S'il vous plaît indiquer le code Perl pour le faire.
La solution
Cela pourrait être un bon point de départ pour un bon programme Perl. Il fait ce que la question a demandé si.
Il était juste piraté ensemble, et ne tient pas compte la plupart des meilleures pratiques pour 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;
}
Après avoir renommé config
à config.rm
, en ajoutant un ombles supplémentaire pour README
, changer un caractère dans install.sh
, et en ajoutant un .test
de fichier. Voici ce qu'il en sortie:
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"
Autres conseils
Voir Archive :: Tar .
Le Archive::Tar
et les modules de File::Find
sera utile. Un exemple de base est illustré ci-dessous. Il imprime simplement des informations sur les fichiers dans un goudron et les fichiers dans une arborescence de répertoires.
Il ne montre pas clairement votre question comment vous voulez comparer les fichiers. Si vous avez besoin de comparer le contenu réel, la méthode get_content()
dans Archive::Tar::File
sera probablement nécessaire. Si une comparaison plus simple est suffisante (par exemple, le nom, la taille et mtime), vous aurez pas besoin beaucoup plus que les méthodes utilisées dans l'exemple ci-dessous.
#!/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 est un peu exagéré pour cela, vraiment. Un script shell ferait bien. Les étapes que vous devez prendre si:
- Extraire le goudron dans un dossier temporaire quelque part.
-
diff -uR
les deux dossiers et rediriger la sortie quelque part (ou peut-être conduite àless
selon le cas) - Nettoyer le dossier temporaire.
Et vous avez terminé. Ne devrait pas être plus de 5-6 lignes. Quelque chose rapide et non testé:
#!/bin/sh
mkdir $TEMP/$$
tar -xz -f ../backups/backup.tgz $TEMP/$$
diff -uR $TEMP/$$ ./ | less
rm -rf $TEMP/$$
Heres un exemple qui vérifie si chaque fichier qui se trouve dans une archive, existe également dans un dossier.
# $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
Voici comment je l'ai testé ceci:
Je retiré / renomme config
, puis couru comme suit:
bash test Downloads/update-dnsomatic-0.1.2.tar.gz Downloads/
Ce qui a donné la sortie:
"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"
Je suis nouveau à bash / programmation shell, donc il est probablement une meilleure façon de le faire.