Perl — способ получить только первое имя файла (.txt) из другого каталога, не загружая их все?

У меня есть каталог, содержащий ~ 5000 файлов .txt размером 2400.

Мне просто нужно одно имя файла из этого каталога; порядок не имеет значения.

Файл будет обработан и удален.

Это не рабочий каталог скриптов.

Намерение:

  • чтобы открыть этот файл,
  • прочитай это,
  • сделать некоторые вещи,
  • отсоедините его, а затем
  • перейти к следующему файлу.

Моя грубая попытка не проверяет только файлы .txt, а также должна получить все ~ 5000 имен файлов только для одного имени файла. Я также, возможно, вызываю слишком много модулей?

Подпрограмма Verify_Empty была предназначена для проверки наличия каталога и файлов в нем, но мои попытки не увенчались успехом, поэтому я ищу помощи.

#!/usr/bin/perl -w
use strict;
use warnings;
use CGI;
use CGI ':standard';
print CGI::header();
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
###
use vars qw(@Files $TheFile $PathToFile);
my $ListFolder = CGI::param('openthisfolder');
Get_File($ListFolder);
###
sub Get_File{
  $ListFolder = shift;
  unless (Verify_Empty($ListFolder)) {
    opendir(DIR,$ListFolder);
    @Files = grep { $_ ne '.' && $_ ne '..' } readdir(DIR);
    closedir(DIR);
    foreach(@Files){
      $TheFile = $_;
    }
    #### This is where I go off to process and unlink file (sub not here) ####
    $PathToFile = $ListFolder.'/'.$TheFile;
    OpenFileReadPrepare($PathToFile); 
    #### After unlinked, the OpenFileReadPrepare sub loops back to this script. 
  }
  else {
    print qq~No more files to process~;
    exit;
  }
  exit;
}
    ####
sub Verify_Empty {
  $ListFolder = shift;
  opendir(DIR, $ListFolder) or die "Not a directory";
  return scalar(grep { $_ ne "." && $_ ne ".." } readdir(DIR)) == 0;
  closedir(DIR);
}

Очевидно, я очень новичок в этом. Этот метод кажется довольно «голодным»? Кажется, это слишком много, чтобы взять одно имя файла и обработать его! Руководство было бы здорово!

EDIT — Последняя попытка

my $dir = '..';
my @files = glob "$dir/*.txt";
for (0..$#files){
$files[$_] =~ s/\.txt$//;
}
my $PathAndFile =$files[0].'.txt';
print qq~$PathAndFile~;

Это «работает», но все равно получает все имена файлов. Пока ни один из приведенных здесь примеров мне не помог. Думаю, я буду жить с этим сегодня, пока не выясню это. Возможно, я вернусь и посмотрю, не придумал ли кто-нибудь что-нибудь лучше.


person OldDogLearningNewPerlTricks    schedule 08.05.2013    source источник
comment
сначала определить? Звучит очень странно, если сказать, что порядок не имеет значения, когда также упоминается первый и последний. Это просто очень сложный способ сказать, что вы хотите перебрать все файлы?   -  person TLP    schedule 08.05.2013
comment
Вместо use vars ( ... $TheFile .. ) и foreach(@Files){ $TheFile = $_; я предпочитаю писать foreach my $TheFile (@Files){. Имеет то преимущество, что дает переменной наименьшую область действия и не использует $_.   -  person AdrianHHH    schedule 08.05.2013
comment
Параметры для вызовов Get_File и Verify_Empty не нужны, так как $ListFolder входит в область действия обоих; их первые присваивания просто перезаписывают переменную уже содержащимся в ней значением.   -  person AdrianHHH    schedule 08.05.2013
comment
Первый и последний значения не имеют. Я НЕ хочу перебирать все файлы. Просто хочу один файл и не важно какой.   -  person OldDogLearningNewPerlTricks    schedule 08.05.2013
comment
Но, @Student33, маркированный список в вашем вопросе указывает [затем] перейти к следующему файлу. Что означает эта фраза, если не то, что все файлы должны быть обработаны?   -  person AdrianHHH    schedule 08.05.2013
comment
Этим занимается другой саб. Я хотел получить только одно имя файла из каталога, не загружая их все. Объяснение всего сценария иногда помогает. Кроме того, мой вопрос был немного отредактирован, поэтому, похоже, эти детали были удалены.   -  person OldDogLearningNewPerlTricks    schedule 09.05.2013
comment
@Student33 Вы обнаружите, что объяснение ВСЕГО вашего сценария, а не его части, помогает больше, чем немного. Ваша текущая попытка, которая сначала получает все файлы, заканчивающиеся на .txt, затем удаляет все окончания .txt, затем снова добавляет окончание .txt... довольно.... странно. Только 1_. Не помещайте их в массив, измените массив, возьмите первый элемент массива и измените его обратно. Это просто глупо.   -  person TLP    schedule 09.05.2013


Ответы (3)


Вы можете зациклить, используя readdir внутри цикла while. Таким образом, readdir не будет возвращать все файлы, а будет давать только один за раз,

# opendir(DIR, ...);
my $first_file = "";
while (my $file = readdir(DIR)) {

  next if $file eq "." or $file eq "..";
  $first_file = $file;
  last;
}
print "$first_file\n"; # first file in directory
person mpapec    schedule 08.05.2013
comment
Я не мог заставить это вернуть какие-либо имена файлов. Файлы находятся не в том же каталоге, что и скрипт, а на один каталог ниже. - person OldDogLearningNewPerlTricks; 09.05.2013
comment
@Student33, ты делаешь что-то не так. Этот фрагмент кода не указывает, какой каталог открыт. Более того, по сути правильно. - person pilcrow; 09.05.2013
comment
Сначала вам нужно открыть каталог, т.е. моя $ListFolder = CGI::param('openthisfolder'); opendir(КАТАЛОГ, $ListFolder); - person mpapec; 09.05.2013

Вы вызываете readdir в контексте списка, который возвращает все записи каталога. Вместо этого вызовите его в скалярном контексте:

my $file;
while( my $entry = readdir DIR ) {

    $file = $entry, last if $entry =~ /\.txt$/;        
}

if ( defined $file ) {
    print "found $file\n";
    # process....
}

Кроме того, вы дважды читаете каталог; один раз, чтобы увидеть, есть ли в нем какие-либо записи, а затем обработать его. Вам действительно не нужно видеть, пуст ли каталог; вы получаете это бесплатно во время цикла обработки.

person Diab Jerius    schedule 08.05.2013
comment
Я не мог заставить это вернуть какие-либо имена файлов. Файлы находятся не в том же каталоге, что и скрипт, а на один каталог ниже. Я пытался изменить безрезультатно. - person OldDogLearningNewPerlTricks; 09.05.2013
comment
@Student33, в этом фрагменте кода тоже не указано, какой каталог открывать, и в принципе он правильный. Вы делаете что-то не так, адаптируя его. - person pilcrow; 09.05.2013
comment
Тесты на равенство '.' и '..' не нужны, так как вы и так уже проверяете /\.txt/. - person pilcrow; 09.05.2013

Если я сильно не ошибаюсь, вы хотите просто перебирать файлы в каталоге, и все эти «первые или последние» и «порядок не имеет значения», а удаление файлов - это просто путаница в том, как это сделать.

Итак, позвольте мне объяснить это очень простым способом и посмотреть, действительно ли это делает то, что вы хотите:

my $directory = "somedir";
for my $file (<$directory/*.txt>) {
    # do stuff with the files
}

glob будет делать то же самое, что и оболочка *nix, она будет отображать файлы с расширение .txt. Если вы хотите провести дальнейшие тесты файлов внутри цикла, это прекрасно.

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

Альтернативой является просто чтение файлов с помощью readdir в цикле while, например, mpapec, упомянутый в его ответе. Преимущество в том, что каждый раз, когда вы читаете новое имя файла, файл будет там. Кроме того, вам не придется хранить в памяти большой список файлов.

person TLP    schedule 08.05.2013
comment
Нет, я не хочу перебирать все файлы. Именно этого я пытаюсь избежать. Мне просто нужно одно имя файла.txt и все. - person OldDogLearningNewPerlTricks; 08.05.2013
comment
Ну, вы хотите случайный файл... Или? Первый означает, что имена файлов отсортированы, но вы говорите, что порядок не имеет значения. - person TLP; 09.05.2013
comment
Правильный. Любое имя файла. Любым способом, который использует наименьшие ресурсы. - person OldDogLearningNewPerlTricks; 09.05.2013
comment
Тогда почему бы тебе просто не сделать my $file = <$dir/*.txt>? Кажется, он надежно выбирает файл из верхней части списка. - person TLP; 09.05.2013