在Perl中原子打开不存在的文件

时间:2012-12-19 13:39:34

标签: perl file-io concurrency

我想写一个名称在变量$filename中的文件。 我不想覆盖它,所以我首先检查它是否存在,然后打开它:

#stage1
if(-e $filename)
{
    print "file $filename exists, not overwriting\n";
    exit 1;
}

#stage2
open(OUTFILE, ">", $filename) or die $!;

但这不是原子的。从理论上讲,有人可以在stage1stage2之间创建此文件。是否有一些open命令的变体会以原子方式执行这两个操作,因此如果文件存在,它将无法打开文件进行写入?

2 个答案:

答案 0 :(得分:6)

以下是打开文件的原子方法:

#!/usr/bin/env perl
use strict;
use warnings qw(all);

use Fcntl qw(:DEFAULT :flock);

my $filename = 'test';
my $fh;

# this is "atomic open" part
unless (sysopen($fh, $filename, O_CREAT | O_EXCL | O_WRONLY)) {
    print "file $filename exists, not overwriting\n";
    exit 1;
}

# flock() isn't required for "atomic open" per se
# but useful in real world usage like log appending
flock($fh, LOCK_EX);

# use the handle as you wish
print $fh scalar localtime;
print $fh "\n";

# unlock & close
flock($fh, LOCK_UN);
close $fh;

调试会话:

stas@Stanislaws-MacBook-Pro:~/stackoverflow$ cat test
Wed Dec 19 12:10:37 2012
stas@Stanislaws-MacBook-Pro:~/stackoverflow$ perl sysopen.pl 
file test exists, not overwriting
stas@Stanislaws-MacBook-Pro:~/stackoverflow$ cat test
Wed Dec 19 12:10:37 2012

答案 1 :(得分:4)

如果您担心修改同一文件的多个Perl脚本,只需使用每个脚本中的flock()函数来锁定您感兴趣的文件。

如果您担心可能无法控制的外部流程,可以使用sysopen()功能。根据 Programming Perl 一书(顺便说一下,我强烈推荐):

  

要解决此覆盖问题,您需要使用sysopen   提供是否创建新文件的单独控制   破坏现有的。我们将放弃–e文件存在测试   因为它在这里没有任何用处,只会增加我们的曝光率   竞争条件。

他们还提供了此示例代码块:

use Fcntl qw/O_WRONLY O_CREAT O_EXCL/;
open(FH, "<", $file)
    || sysopen(FH, $file, O_WRONLY | O_CREAT | O_EXCL)
    || die "can't create new file $file: $!";

在这个例子中,他们首先引入一些常量(用于sysopen调用)。接下来,他们尝试使用open打开文件,如果失败,则会尝试sysopen。他们继续说:

  

现在即使文件以某种方式在打开之间存在   失败,当sysopen尝试打开新文件进行写作时,没有任何伤害   已完成,因为提供了标记,sysopen将拒绝打开   已经存在的文件。

因此,为了清楚地了解您的情况,请完全删除文件测试(不再是第1阶段),使用类似于上面的代码的代码执行打开操作。问题解决了!