2016-10-13 3 views
2

Я запускаю скрипт, который копирует одну папку из определенного места, если она не существует (или несовместима). Проблемы возникают, когда я выполняю этот сценарий 2 раза. Поскольку первый скрипт пытается скопировать файлы, второй приходит и пытается использовать то же самое, что приводит к беспорядку. Как я мог избежать этой ситуации? Что-то вроде системного мьютекса.Обнаружить, что файлы копируются в папку

I судимого простой тест с -w, я вручную скопировать папку и в то время как папка переписывала я запускаю скрипт:

use strict; 
use warnings; 

my $filename = 'd:\\folder_to_copy'; 
if (-w $filename) { 
    print "i can write to the file\n"; 
} else { 
    print "yikes, i can't write to the file!\n"; 
} 

Конечно, это не будет работать, потому что я до сих пор на запись-доступ это папка. Любые идеи о том, как я могу проверить, копируется ли папка в Perl или с использованием batch commands?

ответ

7

Звучит как работа для lock file. Есть мириады модулей CPAN, которые реализуют файлы блокировки, но большинство из них не работают в Windows.Вот несколько, которые, кажется, поддерживают Windows, в соответствии с CPAN испытателей

После быстрого просмотра в исходном коде, только модуль, который я могу порекомендовать, это Файл :: Flock :: Tiny. Остальные кажутся искушенными.

+1

Итак, чтобы узнать, что я все еще копирую в этой папке, я бы сохранил конкретный файл до тех пор, пока не закончу копирование всей папки, и проверка будет в этом файле? –

+0

@JohnDoe Да, вы должны использовать отдельный файл в качестве файла блокировки. – nwellnhof

3

В качестве первого сценария пытается скопировать файлы, то второй приходит и пытается то же самое, что приводит к беспорядке

Простейшим подходом было бы создать файл, который будет содержать 1, если запускается другой экземпляр скрипта. Затем вы можете добавить условие, основанное на этом.

{local $/; open my $fh, "<", 'flag' or die $!; $data = <$fh>}; 
die "another instance of script is running" if $data == 1; 

Другой подход должен был бы установить переменную окружения в сценарии и проверить его в BEGIN блоке.

+3

Это склонное к условиям гонки. – nwellnhof

+2

Спасибо за ответ, это действительно умно, но я могу переместить проблему из папки, как сказал @nwellnhof, в файле содержится флаг. –

4

Если вам нужен общесистемный мьютекс, тогда один «трюк» должен (ab) использовать каталог. Команда mkdir обычно является атомарной и либо работает, либо не работает (если каталог уже существует).

Измените сценарий следующим образом:

my $mutex_dir = '/tmp/my-mutex-dir'; 
if (mkdir $mutex_dir) { 

    # run your copy-code here 

    # when finished: 
    rmdir $mutex_dir; 
} else { 
    print "another instance is already running.\n"; 
} 

Единственное, что вам нужно, чтобы убедиться в том, что вы позволили создать каталог в /tmp (или где).

Обратите внимание, что я намеренно сделать НЕ во-первых тест на наличие $mutex_dir, потому что между if (not -d $mutex_dir) и mkdir кто-то может создать каталог и mkdir потерпит неудачу в любом случае. Поэтому просто позвоните mkdir. Если бы это сработало, вы можете сделать свой материал. Не забудьте удалить $mutex_dir после того, как вы закончите.

Это также недостаток этого подхода: если ваш код копирования сработает, и сценарий преждевременно умирает, каталог не удаляется. Предположительно, механизм блокировки файлов, предложенный в nwellnhof's answer, ведет себя лучше в этом случае и автоматически разблокирует файл.

+0

Есть ли случаи, когда это может закончиться неудачей? Какие-то особые условия? –

+0

Все зависит от атомарности 'mkdir'. В Unix эта операция является атомарной. В Windows я не совсем уверен, и Google перестала быть полезной здесь. Я нашел оба заявления. _If_ 'mkdir' является атомарным под Windows, тогда я не знаю случая, когда этот подход потерпит неудачу (за исключением недостающих разрешений). – PerlDuck

1

Вы можете использовать Windows-мьютекс или Windows, семафоры Объекты пакета http://search.cpan.org/~cjm/Win32-IPC-1.11/

use Win32::Mutex; 
use Digest::MD5 qw (md5_hex); 
my $mutex = Win32::Mutex->new(0, md5_hex $filename); 
if ($mutex) { 
    do_your_job(); 
    $mutex->release 
} else { 
    #fail... 
} 
+0

Это также интересный подход, но я хотел бы придерживаться модулей, которые пришли по умолчанию. –