Perl для проверки подкаталогов и изменения конфигурации

Я пытаюсь написать perl-скрипт, который проверяет все каталоги в текущем каталоге, а затем, соответственно, проникает в последующие каталоги до точки, где он содержит последний каталог. Вот что я написал:

#!/usr/bin/perl -w
use strict;
my @files = <*>;
foreach my $file (@files){
    if (-d $file){
            my $cmd = qx |chown deep:deep $file|;
            my $chdir = qx |cd $file|;
            my @subfiles = <*>:
            foreach my $ subfile(@subfiles){
                if (-d $file){
                    my $cmd = qx |chown deep:deep $subfile|;
                    my $chdir = qx |cd $subfile|;
            .  # So, on in subdirectories
            .
            .
            }
    }

    }
 }

Некоторые каталоги, которые у меня есть, содержат около 50 подкаталогов. Как я могу проникнуть через него, не написав 50 if условий? Пожалуйста, предложите. Спасибо.


person deep    schedule 20.12.2013    source источник
comment
Пожалуйста, можете привести пример. Спасибо.   -  person deep    schedule 20.12.2013
comment
Если по какой-то причине вы хотите накатить собственное решение, вам нужно использовать рекурсию.   -  person flesk    schedule 20.12.2013
comment
Примеры есть в документации File::Find. :)   -  person brian d foy    schedule 20.12.2013
comment
@flesk: нужда - это сильно сказано. Вы можете легко сделать это без рекурсии.   -  person brian d foy    schedule 20.12.2013


Ответы (3)


ПРИМЕЧАНИЕ. Обычно ОС не позволяет вам изменить владельца файла или каталога, если вы не являетесь суперпользователем (т. е. root).

Теперь мы избавились от этого...

Модуль File::Find делает то, что вы хотите. Используйте use warnings; вместо -w:

use strict;
use warnings;
use feature qw(say);
use autodie;
use File::Find;

finddepth sub {
    return unless -d;    #  You want only directories...
    chown deep, deep, $File::Find::name
        or warn qq(Couldn't change ownership of "$File::Find::name\n");
}, ".";

Пакет File::Find импортирует подпрограммы find и finddepth в вашу программу Perl.

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

Имя файла помещается в $_, и вы попадаете в каталог этого файла. Это упрощает выполнение стандартных тестов для файла. Здесь я отвергаю все, что не является каталогом. Это одно из немногих мест, где я буду использовать $_ по умолчанию.

Полное имя файла (из каталога, который вы ищете, помещается в $File::Find::name, а имя каталога этого файла — $File::Find::dir.

Я предпочитаю помещать свою подпрограмму в свой find, но вы также можете поместить туда ссылку на другую подпрограмму. Оба они более или менее эквивалентны:

my @directories;
find sub {
   return unless -d;
   push @directories, $File::Find::name;
}, ".";


my @directories;
find \&wanted, ".";

sub wanted {
   return unless -d;
   push @directories, $File::Find::name;
}

В обоих случаях я собираю имена всех каталогов на моем пути и помещаю их в @directories. Мне нравится первый, потому что он объединяет мою подпрограмму wanted и мой find. Кроме того, загадочно необъявленный @directories в моей подпрограмме не выглядит таким уж загадочным и необъявленным. Я объявил my @directories; прямо над find.

Кстати, именно так я обычно использую find. Я нахожу то, что хочу, и помещаю их в массив. В противном случае вы застрянете, помещая весь свой код в подпрограмму wanted.

person David W.    schedule 20.12.2013

Что ж, способ CS101 (если это просто упражнение) заключается в использовании рекурсивной функции

sub dir_perms {
 $path = shift;
  opendir(DIR, $path);
  my @files = grep { !/^\.{1,2}$/ } readdir(DIR); # ignore ./. and ./..
  closedir(DIR);
  for (@files) {
   if ( -d $_ ) {
    dir_perms($_);
  }
  else {
   my $cmd = qx |chown deep:deep $_|;
   system($cmd);
  }
 }
}

dir_perms(".");

Но я бы также посмотрел на File::Find для чего-то более элегантного и надежного (это может попасть в ловушку циклической ссылки и выдать ошибки, если вы не вызываете его в каталоге и т. д.), и в этом отношении Я бы посмотрел на простой старый UNIX find(1), который может делать именно то, что вы пытаетесь сделать с опцией -exec, например

/bin/bash$ find /path/to/wherever -type f -exec chown deep:deep {} \;
person Bandrami    schedule 20.12.2013

В perldoc File::Find есть примеры того, что вы делаете. Например,

       use File::Find;
       finddepth(\&wanted, @directories_to_search);
       sub wanted { ... }

далее в документе говорится, что вы можете использовать find2perl для создания требуемого {} подпроцесса.

 find2perl / -name .nfs\* -mtime +7 \
           -exec rm -f {} \; -o -fstype nfs -prune
person brianadams    schedule 20.12.2013