¿Cómo puedo comparar la lista de archivos desde un archivo tar y directorio?

StackOverflow https://stackoverflow.com/questions/1271879

  •  13-09-2019
  •  | 
  •  

Pregunta

Todavía estoy aprendiendo Perl. Puede alguien por favor me sugieren el código Perl para comparar los archivos de .tar.gz y una ruta de directorio.

Vamos a decir que tengo copia de seguridad tar.gz de seguir la ruta del directorio que he tomado unos días atrás.

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

Ahora quiero comparar los archivos y directorios en esta ruta con el archivo de copia de seguridad tar.gz..

Para sugerir código Perl para hacer eso.

¿Fue útil?

Solución

Esto podría ser un buen punto de partida para un buen programa de Perl. Hace lo que la pregunta pidió sin embargo.

Se acaba hackeado, e ignora la mayor parte de las mejores prácticas para 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;
}

Después de cambiar el nombre de config a config.rm, la adición de un carbón extra para README, el cambio de un caracter en install.sh, y añadiendo un .test archivo. Esto es lo que da salida:

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"

Otros consejos

El Archive::Tar y módulos File::Find será útil. Un ejemplo básico se muestra a continuación. Simplemente imprime información acerca de los archivos en un alquitrán y los archivos en un árbol de directorios.

No estaba claro de su pregunta de cómo se desea comparar los archivos. Si es necesario comparar el contenido real, será necesario realizar el método get_content() en Archive::Tar::File. Si una comparación más simple es adecuado (por ejemplo, nombre, tamaño y mtime), no será necesario mucho más que los métodos utilizados en el ejemplo siguiente.

#!/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 es una especie de exageración para esto, realmente. Un script de shell haría bien. Los pasos que se deben tomar sin embargo:

  • Extraer el alquitrán en una carpeta temporal en alguna parte.
  • diff -uR las dos carpetas y redirigir la salida en algún lugar (o tal vez tubería para less según sea apropiado)
  • Limpiar la carpeta temporal.

Y ya está. no debe ser más de 5-6 líneas. Algo rápido y no probado:

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

Aquí está un ejemplo que comprueba si todos los archivos que se encuentra en un archivo, también existe en una carpeta.

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

Esta es la forma en que probé esto:

He quitado / config ha cambiado el nombre, entonces encontré lo siguiente:

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

¿Qué le dio la salida:

   "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"

Soy nuevo para golpear la programación / corteza, por lo que es probable que haya una mejor manera de hacer esto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top