Perl readdir однострочный?

На данный момент я знаю два способа открыть и прочитать каталог в Perl. Вы можете использовать opendir, readdir и closedir или просто использовать glob для получения содержимого каталога.


Пример:

Использование opendir, readdir и closedir:

opendir my $dh, $directory;
my @contents = readdir $dh;
closedir $dh;

Использование glob:

my @contents = <$directory/*>;

Мне сообщили, что в некоторых ситуациях метод glob может давать непредсказуемые результаты (например, он может действовать по-разному в разных операционных системах, когда встречает специальные символы в именах каталогов).

Что мне нравится в методе glob, так это то, насколько он «быстрый и грязный». Это одна простая линия, и она выполняет свою работу, но если она не работает во всех ситуациях, это может привести к неожиданным и трудным для поиска ошибкам.

Мне было интересно, существует ли что-то вроде «сокращенного» способа использования метода opendir, readdir, closedir?

Может быть, что-то вроде этого причудливого метода прихлебывания файла в одной строке, которую я недавно обнаружил.


person tjwrona1992    schedule 06.05.2015    source источник
comment
Мне любопытно, что думает человек, который сообщил вам, что произойдет в тех случаях, когда в именах каталогов используются специальные символы.   -  person Andy Lester    schedule 06.05.2015
comment
@AndyLester Я думаю, что OP имеет в виду комментарий о пробелах в именах каталогов. mkdir "foo bar" && touch "foo bar/baz" && perl -E '$dir = "foo bar"; say for <$dir/*>' (можно разрешить, указав аргумент glob)   -  person ThisSuitIsBlackNot    schedule 06.05.2015
comment
Вот отличное описание glob vs. readdir: stackoverflow.com/questions/1506801/   -  person Andy Lester    schedule 06.05.2015
comment
@AndyLester, Обычно glob не находит файлы / каталоги со специальными символами в имени, если специальные символы не были экранированы должным образом. То, как экранируются специальные символы, может зависеть от операционной системы.   -  person tjwrona1992    schedule 06.05.2015
comment
Но теперь, когда я думаю об этом, разве эти специальные символы не могут также сбить с толку opendir?   -  person tjwrona1992    schedule 06.05.2015
comment
sub dir_contents { my ($path) = @_; opendir my $dh, $path or die $!; return readdir $dh };) Я знаю, что это заманчиво, но, пожалуйста, не попадайтесь в ловушку, пытаясь всегда делать что-то одной строкой. Это делает ваш код менее читабельным и сложным для отладки и сопровождения.   -  person ThisSuitIsBlackNot    schedule 06.05.2015
comment
@ tjwrona1992: Я почти уверен, что то, что вы описали, этот glob не найдет файлы / каталоги со специальными символами в имени, неверно. Вы можете посмотреть perldoc File::Glob для получения дополнительной информации.   -  person Andy Lester    schedule 06.05.2015
comment
@ThisSuitIsBlackNot Ну, черт возьми, я могу сделать это как подпрограмму, ха-ха, мне просто интересно, есть ли удобный ярлык, такой как Perl, кажется, предлагает нам для многих других вещей. Я просто подумал, можно ли это сделать с файлами, а почему не с каталогами? :)   -  person tjwrona1992    schedule 06.05.2015
comment
@AndyLester ikegami оставил комментарий к этому удаленному вопросу. Я этого не вижу, но вы должны увидеть. Я почти уверен, что упоминались пробелы, но я ничего не помню о других специальных символах.   -  person ThisSuitIsBlackNot    schedule 06.05.2015
comment
Это точный комментарий: my @contents = ‹$ directory / *›; глючит. Например, это не сработает, если $ directory содержит пробелы или ряд других символов. Даже если вы добавите необходимое экранирование (которое зависит от ОС), * не вернет все файлы в каталоге в системах unix.   -  person tjwrona1992    schedule 06.05.2015
comment
@ Энди Лестер, в этом нет смысла; нет разницы между <...> и glob(qq<...>) (кроме случаев, когда <...> означает readline(...), конечно).   -  person ikegami    schedule 06.05.2015
comment
Насколько я понимаю, glob() была более безопасной версией <>. Моя вина.   -  person Andy Lester    schedule 06.05.2015


Ответы (2)


Как насчет следующего?

my @contents = get_dir_contents($dir);

Вы даже можете решить, как это обрабатывает ошибки, нужно ли возвращать ., нужно ли возвращать .., должны ли возвращаться «скрытые» файлы и добавляется ли путь к имени файла, поскольку вы сами пишете get_dir_contents.


Альтернативы:

  • use File::Find::Rule qw( );
    my @contents = File::Find::Rule->maxdepth(1)->in($dir);
    
  • use File::Slurp qw( read_dir );
    my @contents = read_dir($dir);
    
  • # Unix only, but that includes cygwin and OS/X.
    my @contents = <\Q$dir\E/.* \Q$dir\E/*>;
    
person ikegami    schedule 06.05.2015
comment
Мне нравится File::Slurp::read_dir, хотя я понимаю, что File::Slurp потерял популярность в некоторых кругах. - person Jim Davis; 06.05.2015
comment
@Jim Davis, File :: Slurp раньше чертовски глючил, поэтому я всегда избегал этого. Я даже не знал, что у него такая подлодка. Однако, насколько я понимаю, эти проблемы были устранены давно. Я добавил это к своему ответу. - person ikegami; 06.05.2015
comment
@ikegami, Подпрограмма - очевидное решение, это скорее вопрос теоретический. Мне нужен самый простой способ чтения каталога. По сути, это просто даст вам результат по умолчанию readdir без всей громоздкости, связанной с наличием строки opendir для получения дескриптора, за которой следует строка readdir для чтения дескриптора, за которой следует строка closedir, чтобы закрыть дескриптор. - person tjwrona1992; 06.05.2015
comment
Я дал вам четыре таких решения. - person ikegami; 07.05.2015
comment
В каком пакете находится get_dir_contents? - person felwithe; 05.11.2017
comment
@felwithe, в какой бы пакет вы его ни решили, раз уж вы его пишете. - person ikegami; 05.11.2017
comment
@ikegami, это не ответ. - person felwithe; 05.11.2017

Я полагаю, что придумал действительный однострочный текст, который включает _1 _ / _ 2_!

my @contents = do { opendir my $dh, $dir or die $!; readdir $dh };

Это должно открыть и прочитать каталог, вернуть все его содержимое, и как только блок do закончится, $dh должен быть закрыт Perl "автоматически".

person tjwrona1992    schedule 06.05.2015
comment
Чем это существенно отличается от opendir my $dh, $dir; my @contents = readdir $dh;? Вам не нужно do помещать несколько операторов в одну строку. - person ThisSuitIsBlackNot; 06.05.2015
comment
do {} делает my ($dh) локальной областью видимости, так что дескриптор файла будет закрыт, как только он выйдет за пределы области видимости. Это также позволяет объявлению my @contents вне этой области видимости, поэтому его можно объявить в той же строке и по-прежнему использовать нелокально. Если вы попытаетесь выполнить его локально без do {}, вы получите { opendir my $dh, $dir; my @contents = readdir $dh; }, но тогда вы столкнетесь с проблемой невозможности использовать @contents в следующей строке, потому что теперь это будет вне области видимости! ... На самом деле, довольно красиво и изящно. Perl потрясающий. :) - person tjwrona1992; 06.05.2015
comment
Не закрывать дескриптор лексического каталога немедленно - это редко имеет значение, поэтому я не думаю, что то, что вы опубликовали, существенно отличается от простого размещения нескольких операторов в одной строке. Но поскольку вы уже знаете, что это плохая практика, я перестану твердить об этом :) - person ThisSuitIsBlackNot; 06.05.2015
comment
OP специально просил что-то короче _1 _ + _ 2 _ + _ 3_. Технически удаление closedir короче, но разве вы не думаете, что ОП знал об этом? Кроме того, уже были предоставлены четыре других более коротких решения, поэтому я не уверен, как вы можете считать это сокращением. - person ikegami; 07.05.2015
comment
Создание собственной подпрограммы вряд ли можно назвать сокращением, а использование другого модуля почти похоже на написание собственной подпрограммы (за исключением того, что она была протестирована, и вы знаете, что она будет работать). Из ваших ответов использование File :: Slurp, вероятно, является самой короткой и простой реализацией, но цель этого вопроса заключалась в том, чтобы найти свой собственный способ сделать это. Это больше об обучении, чем о реальном удобстве использования. В принципе, я знал, что могу получить его в одну строку, используя glob, но я хотел знать, есть ли короткая альтернатива глобусу с использованием _1 _ / _ 2 _ / _ 3_. - person tjwrona1992; 07.05.2015
comment
Кроме того, причина, по которой я оставил or die в моем первоначальном ответе, заключалась в том, чтобы подчеркнуть, насколько красивым и коротким был код, :) очевидно, что добавление or die - лучшая практика. - person tjwrona1992; 07.05.2015