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

nuo*_*eri 9 concurrency perl file-io

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

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

#stage2
open(OUTFILE, ">", $filename) or die $!;
Run Code Online (Sandbox Code Playgroud)

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

cre*_*ive 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;
Run Code Online (Sandbox Code Playgroud)

调试会话:

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
Run Code Online (Sandbox Code Playgroud)


Jon*_*hop 3

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

\n\n

如果您担心您可能无法控制的外部进程,则可以使用sysopen()函数。根据《Programming Perl》一书(顺便说一句,我强烈推荐这本书):

\n\n
\n

要解决这一覆盖问题,您\xe2\x80\x99 需要使用sysopen,它提供了对是否创建新文件或破坏现有文件的单独控制。我们\xe2\x80\x99将放弃该\xe2\x80\x93e文件存在测试\n,因为它在这里没有任何用处,只会增加我们对竞争条件的暴露\n。

\n
\n\n

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

\n\n
use Fcntl qw/O_WRONLY O_CREAT O_EXCL/;\nopen(FH, "<", $file)\n    || sysopen(FH, $file, O_WRONLY | O_CREAT | O_EXCL)\n    || die "can\'t create new file $file: $!";\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
\n

现在,即使文件在打开失败和尝试打开新文件进行写入之间以某种方式出现sysopen,也不会造成任何损害,因为使用提供的标志,sysopen将拒绝打开已存在的文件。

\n
\n\n

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

\n