我正在尝试编写一个程序,其中perl打开一个文件,但如果该文件不存在或由于某种原因无法打开,则会回退到另一个文件。相关的一行是:
open(my $fh,"<","/path/to/file") or open (my $fh,"<","/path/to/alternate/file") or die
最终,我发现了:
open(my $fh,"<","/path/to/file") or open ($fh,"<","/path/to/alternate/file") or die
的工作。这两个陈述之间有什么区别,为什么不是第一个工作,第二个是正确的方法,还是有一些问题呢?
编辑:如果重要,我使用perl 5.12
,第一个在"/path/to/file"
存在的情况下失败。我倾向于,如果第一次打开成功,第二个open
不应该运行,为什么$fh
被第二个被覆盖?
答案 0 :(得分:9)
我声明了一个变量。如果在相同的范围内使用两次相同的名称,稍后会提到它将是第二个,而不是第一个。您的代码将触发"my" variable ... masks earlier declaration in the same statement
警告(如果您应该启用警告。)因此,如果第一次打开成功,它会设置一个稍后无法访问的$fh
变量,并且第二个变量将保留处于未记录的未定义状态,因为它的声明实际上并未执行。 (请参阅perldoc perlsyn中的“这里是龙”警告,并意识到A or B
相当于B unless A
。)
你的“工作”代码也被打破了;虽然my
返回新声明的变量,然后可以设置,但是词法的范围(后面提到它的变量找到变量)直到下面的语句才开始。所以你的第一个$fh
是将在以后的行上访问的词法,但第二个实际上是一个全局变量(或者如果你使用严格的话,那么就是错误)。
正确的代码是:
my $fh;
open $fh, ... or open $fh, ...;
答案 1 :(得分:4)
其他人已经说过为什么现有代码不起作用,但也提供了具有竞争条件的版本:文件的状态可能会在您检查它和打开它之间发生变化。在你的情况下它是相当温和的,但它可以产生微妙的错误和安全漏洞。通常,您可以通过尝试打开文件来检查是否可以打开文件。
这是一种更通用的方法,可以扩展到多个文件,让您知道打开了哪个文件,并且不包含竞争条件。
use Carp;
sub try_open {
my @files = @_;
for my $file (@files) {
if( open my $fh, "<", $file ) {
return { fh => $fh, file => $file };
}
}
croak "Can't open any of @files";
}