Мой взгляд на проблему: прочитайте файл и, если найдены имена файлов (судя по .txt
), откройте и прочитайте их, и продолжайте рекурсивно.В опубликованном коде есть несколько основных ошибок, которые обсуждаются ниже.
Я предполагаю, что сначала должны быть напечатаны все строки файла, а затем мы переходим к следующему файлу (файлам) (если он найден).Код ниже позволяет закрывать файловые дескрипторы;за ним следует небольшое изменение, которое хранит их в массиве и открывает их.
use warnings;
use strict;
use feature 'say';
my $file = shift @ARGV || 'file.txt';
open my $fh, '<', $file or die "Can't open $file: $!";
recurse_open($fh);
sub recurse_open {
my ($fh) = shift;
my @files;
while (<$fh>) {
print;
if (/\b(.+?\.txt)\b/) {
push @files, $1;
}
}
say '---';
foreach my $file (@files) {
open my $fh_next, '<', $file or do {
warn "Can't open $file: $!";
next;
};
recurse_open($fh_next);
}
}
Это печатает
main file
file1.txt is in it
end of main file
---
file one, with
a line with file2.txt
end of one
---
file two, which has
a line with file3.txt
end of two
---
Just the file3,
no more filenames.
---
, где содержимое file.txt
и файлы 1..3понятно, надеюсь (отделено ---
).Это следует за всеми именами файлов, присутствующими в файле, если случается, что их больше одного.
Если фраза " без уничтожения файловых дескрипторов " в заголовке означает, что файловые дескрипторы должныбыть открытым (и собранным), а затем просто добавлять их в массив по мере их открытия
open my $fh, '<', $file or die "Can't open $file: $!";
my @filehandles = ($fh);
recurse_open($fh, \@filehandles);
sub recurse_open {
my ($fh, $handles) = @_;
...
foreach my $file (@files) {
open my $fh_next, '<', $file or do {
warn "Can't open $file: $!";
next;
};
push @$handles, $fh_next;
recurse_open($fh_next, $handles);
}
}
Обычно (лексический) дескриптор файла закрывается, когда он выходит из области видимости.Однако, поскольку каждый из них теперь копируется в массив, определенный в большей области, они сохраняются, поскольку для каждого есть ссылка.
Комментарии к коду в вопросе.
Самая серьезная ошибка - это очевидное неправильное понимание того, что такое файловый дескриптор и что делает.Выражение <$fh>
считывает из файла, который был связан с дескриптором файла $fh
, когда он был открыт, где <>
- версия оператора readline .См. Операторы ввода / вывода в perlop .
Возвращает строку в файле, и , что - это то, над чем вы должны работать, с chomp
, m//
и т. д., а не на самом $fh
.При while (<$fh>)
(в условии ничего больше) строка присваивается специальной переменной $_
, которая по умолчанию для многих вещей в Perl.Приведенный выше код использует это.
Далее, вы фактически не сопоставляете и не фиксируете имя файла, а только сопоставляете только .txt
.(Это совпадение использует файловый дескриптор вместо переменной, содержащей строку, а open
использует этот файловый дескриптор вместо имени файла, что является путаницей файлового дескриптора, упомянутой выше.)
Тогда я не вижунужно для этого танцевать около $set
, увеличивая и уменьшая его.Поскольку вы прекрасно перенесли все это в подпрограмму, просто используйте дескриптор файла в переменной.Так что я покончил с массивом.Пожалуйста, восстановите его, если это необходимо по каким-то другим причинам.
Наконец:
Всегда Запустите программу с use warnings;
и use strict;
,Это не какая-то педантичность, но напрямую помогает отлавливать ошибки и применяет некоторые очень хорошие практики.
Всегда проверяйте open
звоните (open ... or ...
)
Используйте лексические дескрипторы файлов (my $fh
) вместо globs (FH
), они намного лучше.Используйте версию с тремя аргументами open
Если это целая цель, вы можете также передать имя файла рекурсивной подпрограмме и открыть его и прочитать файл.