Проблемы с Perl и tar - PullRequest
       25

Проблемы с Perl и tar

1 голос
/ 03 января 2012

Я пишу этот скрипт резервного копирования:

#!/usr/bin/perl
use strict;
use POSIX qw(strftime);
use DBI;

my $destdir       = "/mnt/backup";
my $tmpdir        = "/mnt/backup/tmp";
my $mysqlpassword = "";

my %backup = (
    '/home' => [
        'path/to/exclude/from/backup/*',
    ],
    '/etc',
);

my $now = strftime("%Y-%m-%d-%s", localtime);
my $tmpbdir = "$tmpdir/backup_$now";

sub printlog
{
    print "[", strftime("%D %T", localtime), "] $_[0]\n";
}

sub backup
{
    foreach my $dir (keys %backup) {
        printlog "Backing up $dir...";
        my $excludes = "";
        if ($#{$backup{$dir}}+1 > 0) {
            for my $i (0 .. $#{$backup{$dir}}) {
                $excludes = "--exclude $dir/$backup{$dir}[$i] ";
            }
            $excludes =~ s/\s$//;
        }
        system("tar -cJf $excludes $tmpbdir/$dir.xz $dir");
        printlog "$dir backup complete.";
    }
}

sub backup_mysql
{
    printlog "Backing up MySQL...";
    mkdir("$tmpbdir/mysql");
    my $dbh = DBI->connect('dbi:mysql:information_schema', 'root', $mysqlpassword, {'RaiseError' => 1});
    if ($dbh) {
        my $sth = $dbh->prepare('SELECT SCHEMA_NAME AS `Database` FROM INFORMATION_SCHEMA.SCHEMATA'); $sth->execute;
        while (my @databases = $sth->fetchrow_array) {
            system("mysqldump --opt --single-transaction --user=root --password=$mysqlpassword $databases[0] > $tmpbdir/mysql/$databases[0].sql");
        }
        system("tar -cjf $tmpbdir/mysql.xz $tmpbdir/mysql");
        system("rm -rf $tmpbdir/mysql");
        printlog "MySQL backup complete.";
    } else {
        print "Unable to connect to MySQL server.\n$DBI::errstr\n";
    }
}

sub archive
{
    printlog "Archiving...";
    system("tar -cf $destdir/backup_$now.tar $tmpbdir/*");
    system("rm -rf $tmpbdir");
    printlog "Backup complete in $destdir/backup_$now.tar";
}

if ($> != 0) {
    die "You must run this script as root.\n";
}

printlog "Backup starts.";
mkdir($tmpbdir);

&backup;
&backup_mysql;
&archive;

Но когда я запускаю скрипт, резервное копирование / home завершается неудачно. Это вывод сценария.

[...]
[01/03/12 03:00:03] Backing up /home...
tar: Removing leading `/' from member names
tar: /mnt/backup/tmp/backup_2012-01-03-1325556001//home.xz: Cannot stat: No such file or directory
tar: /home/minecraft/world/region/r.-1.1.mcr: file changed as we read it
tar: /home/minecraft/world: file changed as we read it
tar: /home/webserver/ubuntubarsport/log/access_log: file changed as we read it
tar: Removing leading `/' from hard link targets
tar: Exiting with failure status due to previous errors
[...]

Я не знаю причину tar: /mnt/backup/tmp/backup_2012-01-03-1325556001//home.xz: Cannot stat: No such file or directory. Что я делаю неправильно? Резервная копия / etc работает нормально. Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 04 января 2012

Используйте следующие модули в своем Perl-скрипте вместо того, чтобы использовать system call:

  • Используйте File :: Find , чтобы найти файлы, которые вы хотите сделать резервную копию.
  • Используйте Archive :: Tar для создания своего архива.

Это устраняет возможные проблемы несовместимости системы, и вы получите больший контроль над тем, что вы 'Резервное копирование и где.Да, с вашей стороны потребуется немного больше смазки для локтя.Вам придется изучить эти два модуля, если вы не знакомы с ними, но оба эти модуля были включены в Perl начиная с Perl 5.10.И, если вы используете более старую версию Perl, вы можете установить их из CPAN.

В конце вы, вероятно, найдете это быстрее и более гибким.Кроме того, это правильный способ решения проблемы (т. Е. Использование модулей Perl вместо оболочки для команд операционной системы.

И, поскольку теперь вы пишете все на чистом Perl,у вас нет проблемы с тем, как ваша команда system взаимодействует с командной строкой или какая версия tar установлена ​​на компьютере.

File :: Find довольно прост ( NOTE : код не проверен):

use File::Find;

my @dirList = qw(list of directories to go through);
my @file_list;

find ({
   push @file_list, $File::Find::name if -f; #Backup files only
}, @dirList);

Это должно поместить ваши файлы в @file_list. Подпрограмма в find может использоваться для фильтрации всего, что вам не нужно. И вы можетеиспользуйте ссылку на подпрограмму тоже. Следующее - то же, что и выше.

find (\&wanted, @dir_list);

sub wanted {
   if (-f) {   #Use the `if` to see whether you want to backup a file.
       push @file_list, $File::Find::name;
   }
}

Я немного более шаткий на Archive::Tar, но, если я помню, это несколько этапов:

  • Используйте my $object = Archive::Tar->new для создания объекта .
  • Используйте $object->create_archive для создания нового архива.
  • Используйте $object->add_files для добавленияфайлы, которые вы хотите в своем архиве.
  • Когда вы закончите, используйте $object->write, чтобы записать архив на диск и закрыть $object

Попробуйте, это не так уж сложно, и вы улучшите свои навыки программирования на Perl.

1 голос
/ 03 января 2012

Вам не хватает каталога, в котором вы пытаетесь создать резервный архив. Добавить

system("mkdir -p $tmpbdir");

к началу резервного копирования ().

EDIT

Также резервный архив должен находиться в командной строке сразу после -f, поэтому вместо

system("tar -cJf $excludes $tmpbdir/$dir.xz $dir");

у вас должно быть

system("tar $excludes -cJf $tmpbdir/$dir.xz $dir");

EDIT

Удалить косую черту из исключений:

'home/webserver/ubuntubarsport/www/minecraft/*'

А если вам не нужен сам этот каталог, также следует косая черта и звездочка:

'home/webserver/ubuntubarsport/www/minecraft'

Имейте в виду, что tar --exclude ожидает шаблон.

...