9

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

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

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

但这不是原子的。理论上有人可以在stage1和之间创建这个文件stage2。是否有一些open命令变体可以以原子方式完成这两件事,因此如果文件存在,它将无法打开文件进行写入?

4

2 回答 2

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
于 2012-12-19T14:13:52.717 回答
3

如果您担心多个 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将拒绝打开已经存在的文件。

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

于 2012-12-19T14:05:46.900 回答