#regex #perl
#регулярное выражение #perl
Вопрос:
У меня есть требование найти имя файла, отобразить текст, если он присутствует, и ниже приведен код, который я использую
use strict;
use warnings;
use feature "say";
my @arr={"file1*.gz","file2*.gz"};
foreach my $file (@arr) {
my $file1=glob ("$file");
say "$file1";
if (-e $file1) {
say "File Generated using script";
}
}
Когда я использую приведенный ниже код, я могу правильно получить 1-й элемент массива, но для 2-го элемента я вижу ошибку ниже:
Use of uninitialized value $file1 in string
И если размер массива равен 1, то он работает правильно.
Я не уверен, что происходит не так в приведенном выше коде.
Ответ №1:
Существует проблема с этой строкой:
my @arr={"file1*.gz","file2*.gz"};
Я думаю, вы хотели использовать круглые скобки вместо завитушек для создания своего массива; у вас есть ссылка на хэш.
Существует также проблема с этой строкой:
my $file1=glob ("$file");
Вместо того, чтобы возвращать имя файла, glob возвращает undef
второй раз, потому что вы используете его в скалярном контексте и:
В скалярном контексте glob перебирает такие расширения имени файла, возвращая undef, когда список исчерпан.
Вы можете использовать glob
в контексте списка, который может быть принудительно заключен в круглые скобки $file
:
use strict;
use warnings;
use feature "say";
my @arr = ("file1*.gz", "file2*.gz");
foreach my $file (@arr) {
my ($file1) = glob($file);
say $file1;
if ( -e $file1 ) {
say "File Generated using script";
}
}
Этот код захватывает только первое имя файла, которое соответствует вашему шаблону. Если вы хотите, чтобы все файлы совпадали, вам нужно добавить еще for
один цикл.
Ответ №2:
Вы не используете всю мощь glob
функции. Как говорит toolic, вы также неправильно используете фигурные скобки {}
— они создают ссылку на хэш, а не список.
Ваши параметры при обычном назначении массива обычно:
my @arr = ('foo', 'bar'); # parenthesis
my @arr = qw(foo bar); # "quote word", basically a string split on space
Но это не имеет отношения к ответу на ваш вопрос: как найти имена существующих файлов, которые соответствуют глобальному выражению.
Во-первых, аргумент to glob
может содержать несколько шаблонов, разделенных пробелом. Например, из документации perldoc -f glob:
…
glob("*.c *.h")
соответствует всем файлам с.c
.h
расширением или.
Вы должны прочитать всю документацию, она очень поучительна и обладает основными знаниями Perl.
Таким образом, вам не нужно перебирать количество шаблонов глобусов, просто объедините их. Например.
my $globs = "file1*.gz file2*.gz"; # two patterns at once
Но это еще не все. Вы можете использовать фигурные скобки в глобусах, создавая чередование. Например, {1,2}
создаст два чередования с 1 и 2 соответственно, чтобы мы могли еще больше упростить ваше выражение
my $globs = "file{1,2}*.gz"; # still two patterns at once
И это еще не все. Вам не нужно хранить эти шаблоны глобусов в массиве и перебирать его, вы можете просто перебирать сам результат глобуса. Например.
for my $file (glob $globs) { # loop over globs
Вам также не нужно проверять, существует ли файл с -e
помощью, поскольку глобус уже позаботится об этой проверке. Если глобус не возвращает имя файла, он не был найден. Это работает почти так же, как использование глобусов в командной строке в bash.
Короче говоря, вы можете использовать что-то вроде этого:
use strict;
use warnings;
use feature "say";
foreach my $file (glob "file{1,2}*.gz") {
say "File '$file1' found";
}
Ответ №3:
Возможно, OP предполагал что-то вроде
use strict;
use warnings;
use feature 'say';
my @patterns = qw/file1*.gz file2*.gz/;
for my $pat (@patterns) {
say 'Pattern: ' . $pat;
for my $fname ( glob($pat) ) {
say "t$fname :: File Generated using script" if -e $fname;
}
}
Комментарии:
1. Глобус покажет, существует ли имя файла. Ваше
if
утверждение избыточно. Кроме того, ваш код дублирует то, что уже пытался сделать OP.2. @TLP —
if
оператор взят из кода OP (возможно, OP следует исследовать использованиеglob
)