2014-10-29 3 views
1

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

/dir/here/tmp1/recursive/foo2013.log 
/dir/here/tmp1/recursive/foo2014.log 
/dir/here/tmp2/recursive/foo2013.log 
/dir/here/tmp2/recursive/foo2014.log 

, где в 2013 и 2014 годах говорится, в каком году файлы были изменены.

Я хочу найти более актуальные файлы (foo2014.log) для каждого дерева каталогов (tmp1 и tmp2 аналогичным образом).

Ссылаясь на this answer У меня есть следующий код в script.pl:

#!/usr/bin/perl 
use strict; 
use warnings; 
use File::Find; 

func("tmp1"); 
print "===\n"; 
func("tmp2"); 

sub func{ 
    my $varName = shift; 
    my %times; 
    find(\&upToDateFiles, "/dir/here"); 

    for my $dir (keys %times) { 
     if ($times{$dir}{file} =~ m{$varName}){ 
      print $times{$dir}{file}, "\n"; 
      # do stuff here 
     } 
    } 

    sub upToDateFiles { 
     return unless (-f && /^foo/); 
     my $mod = -M $_; 
     if (!defined($times{$File::Find::dir}) 
     or $mod < $times{$File::Find::dir}{mod}) 
     { 
      $times{$File::Find::dir}{mod} = $mod; 
      $times{$File::Find::dir}{file} = $File::Find::name; 
     } 
    } 
} 

, который даст мне этот выход:

Variable "%times" will not stay shared at ./script.pl line 25. 
/dir/here/tmp1/recursive/foo2014.log 
=== 

У меня есть три вопроса:

  1. Почему не второй вызов функции func работает как первый? Переменные определяются только в рамках функции, поэтому почему я получаю помехи?

  2. Почему я получаю уведомление по переменной %times и как я могу избавиться от него?

  3. Если я определяю функцию upToDateFiles вне func я получаю эту ошибку: Execution of ./script.pl aborted due to compilation errors. Я думаю, это потому, что переменные не определены вне func. Возможно ли это изменить и получить желаемый результат?

+1

Вы можете вместо 'sub upToDateFiles {...' использовать 'my $ upToDateFiles = sub {...', определить перед вызовом find и использовать его как 'find ($ upToDateFiles, ...'. – Qtax

+0

@Qtax Thx, это работает как прелесть! Я был бы рад, если бы вы могли подробно объяснить/объяснить и предложить ответ. Я мог бы также увеличить его тогда :) – EverythingRightPlace

ответ

3

Для начала - встраивание вспомогательного устройства в другое вспомогательное устройство довольно неприятно. Если вы use diagnostics; вы получите:

(W closure) An inner (nested) named subroutine is referencing a 
lexical variable defined in an outer named subroutine. 

When the inner subroutine is called, it will see the value of 
the outer subroutine's variable as it was before and during the *first* 
call to the outer subroutine; in this case, after the first call to the 
outer subroutine is complete, the inner and outer subroutines will no 
longer share a common value for the variable. In other words, the 
variable will no longer be shared. 

This problem can usually be solved by making the inner subroutine 
anonymous, using the sub {} syntax. When inner anonymous subs that 
reference variables in outer subroutines are created, they 
are automatically rebound to the current values of such variables. 

Который имеет непосредственное отношение к проблеме, которую вы имеете. Попытайтесь избежать вложенности ваших подсайтов, и у вас не будет этой проблемы. Это, конечно, похоже, что вы пытаетесь быть намного сложнее, чем вам нужно. Рассматривали ли вы что-то вроде:

#!/usr/bin/perl 
use strict; 
use warnings; 
use diagnostics; 

use File::Find; 

my %filenames; 

sub compare_tree { 
    return unless -f && m/^foo/; 

    my $mtime = -M $File::Find::name; 

    if (!$filenames{$_} || $mtime < $filenames{$_}{mtime}) { 
     $filenames{$_} = { 
      newest => $File::Find::name, 
      mtime => $mtime, 
     }; 
    } 
} 

find(\&compare_tree, "/dir/here"); 

foreach my $filename (keys %filenames) { 
    print "$filename has newest version path of:", $filenames{$filename}{newest}, "\n"; 
    print "$filename has newest mtime of:",  $filenames{$filename}{mtime}, "\n"; 
} 

Я бы также отметить, - вы, кажется, использует $File::Find::dir - это выглядит не так со мной, основываясь на том, что вы описали, что вы делаете. Аналогично - вы запускаете find дважды в той же структуре каталогов, что не очень эффективный подход - очень большие находки - дорогостоящие операции, поэтому удвоение требуемой работы не очень хорошо.

Редактировать: Пойман, забыв, что -M был: -M Script start time minus file modification time, in days.. Поэтому «новые» файлы - это меньшее число, а не более высокое. (Поэтому внесли поправки соответственно).

+0

Это не работает. Он дает предупреждение плюс вывод не является желаемым. – EverythingRightPlace

+1

Подсказка, избегая вложенных подложек, хороша. Я попросил его в пункте 3). Тем не менее я не знаю, как это сделать правильно. – EverythingRightPlace

+1

Прервано из-за ошибок, связанных с ошибкой, ошибка, которую вы получаете _after_, она дает вам другие предупреждения. Включите 'use diagnostics' и посмотрите, что он говорит. – Sobrique

Смежные вопросы