TAR 아카이브 및 디렉토리에서 파일 목록을 어떻게 비교할 수 있습니까?
문제
나는 아직도 Perl을 배우고 있습니다. 누구든지 .tar.gz와 디렉토리 경로에서 파일을 비교하기 위해 Perl 코드를 제안 할 수 있습니까?
내가 며칠 전에 걸렸던 다음 디렉토리 경로의 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 코드를 제안하십시오.
해결책
이것은 좋은 Perl 프로그램에 좋은 출발점 일 수 있습니다. 그래도 질문이 요구 한 일을합니다.
방금 해킹 당했고 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;
}
변경 후 config
에게 config.rm
, 추가 숯을 추가합니다 README
, 숯을 변경합니다 install.sh
, 및 파일 추가 .test
. 이것이 출력 한 것입니다.
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"
다른 팁
보다 아카이브 :: 타르.
그만큼 Archive::Tar
그리고 File::Find
모듈이 도움이 될 것입니다. 기본 예는 다음과 같습니다. 타르의 파일과 디렉토리 트리의 파일에 대한 정보를 인쇄합니다.
파일을 어떻게 비교하고 싶은지 질문에서 명확하지 않았습니다. 실제 콘텐츠를 비교 해야하는 경우 get_content()
방법 Archive::Tar::File
필요할 것입니다. 더 간단한 비교가 적절한 경우 (예 : 이름, 크기 및 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;
Perl은 실제로 이것에 대한 과잉입니다. 쉘 스크립트는 잘 될 것입니다. 그래도 수행해야 할 단계 :
- 타르를 어딘가에 임시 폴더로 추출하십시오.
diff -uR
두 폴더를 두 개의 폴더와 어딘가에서 출력을 리디렉션합니다 (또는 파이프less
적절한)- 임시 폴더를 정리하십시오.
그리고 당신은 끝났습니다. 5-6 줄을 넘지 않아야합니다. 빠르고 테스트되지 않은 것 :
#!/bin/sh
mkdir $TEMP/$$
tar -xz -f ../backups/backup.tgz $TEMP/$$
diff -uR $TEMP/$$ ./ | less
rm -rf $TEMP/$$
그녀는 아카이브에있는 모든 파일이 폴더에도 존재하는지 확인하는 예입니다.
# $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 test Downloads/update-dnsomatic-0.1.2.tar.gz Downloads/
출력을 제공했습니다.
"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"
나는 Bash / Shell 프로그래밍을 처음 접 했으므로 아마도 더 좋은 방법이있을 것입니다.