2016-12-07 4 views
7

Я пытался написать программу, в которой perl открывает один файл, но возвращается к другому, если этот файл не существует или не может быть открыт по какой-либо причине. Соответствующая линия:Fallback Open File 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

В первую очередь не было проблем, хотя переменная повторно объявлена ​​повторно. –

+0

@JayKumarR Ну, это не сработало. В следующей строке '$ fh' не был дескриптором открытого файла для любого файла. – Chris

+1

Всегда используйте 'use strict; использовать предупреждения qw (все); '!!!! – ikegami

ответ

8

my объявляет переменную. Если вы используете его дважды с тем же именем в той же области действия, позже упоминания о нем будут вторыми, а не первыми. Ваш код вызовет предупреждение "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, ...; 
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"; 
}