Вместо того, чтобы кататься на своем приключении для обработки каталогов, почему бы просто не использовать File::Find, чтобы пройти через структуру каталогов для вас.
#! /usr/bin/env perl
use :5.10;
use warnings;
use File::Find;
use File::Path qw(make_path);
use File::Copy;
use Cwd;
# The first two arguments are source and dest
# 'shift' pops those arguments off the front of
# the @ARGV list, and returns what was removed
# I use "cwd" to get the current working directory
# and prepend that to $dest_dir. That way, $dest_dir
# is in correct relationship to my input parameter.
my $source_dir = shift;
my $dest_dir = cwd . "/" . shift;
# I change into my $source_dir, so the $source_dir
# directory isn't in the file name when I find them.
chdir $source_dir
or die qq(Cannot change into "$source_dir");;
find (sub {
return unless -f; #We want files only
make_path "$dest_dir/$File::Find::dir"
unless -d "$dest_dir/$File::Find::dir";
copy "$_", "$dest_dir/$File::Find::dir"
or die qq(Can't copy "$File::Find::name" to "$dest_dir/$File::Find::dir");
}, ".");
Теперь вам не нужна process_files
подпрограммы. Вы разрешаете дескриптор каталога File::Find::find
.
Кстати, вы можете переписать find
как это, которое, как вы обычно видите его в documentation:
find (\&wanted, ".");
sub wanted {
return unless -f; #We want files only
make_path "$dest_dir/$File::Find::dir"
unless -d "$dest_dir/$File::Find::dir";
copy "$_", "$dest_dir/$File::Find::dir"
or die qq(Can't copy "$File::Find::name" to "$dest_dir/$File::Find::dir");
}
Я предпочитаю, чтобы встроить мой хотел подпрограммы в мою find
команды вместо потому что я думаю он выглядит лучше. Это в первую очередь гарантирует, что требуется подпрограмма с командой find
. Вам не нужно смотреть на два разных места, чтобы посмотреть, что происходит.
Кроме того, команда find
имеет тенденцию поглощать всю вашу программу. Представьте, где я получаю список файлов и выполняю над ними сложную обработку. Вся программа может оказаться в подпрограмме wanted
. Чтобы избежать этого, вы просто создаете массив файлов, которые вы хотите работать дальше, а затем работать с ними в программе:
...
my @file_list;
find (\&wanted, "$source_dir");
for my $file (@file_list) {
...
}
sub wanted {
return unless -f;
push @file_list, $File::Find::name;
}
Я считаю это программирование мерзости. Прежде всего, что происходит с find
? Он меняет @file_list
, но как? Нет, где в команде find
указан @file_list
. Что он делает?
Тогда в конце моей программы есть функция sub wanted
, которая использует переменную @file_list
в глобальном режиме. Это плохая практика программирования.
Встраивание моей подпрограммы непосредственно в мою find
команду решает многие из этих вопросов :
my @file_list;
find (sub {
return unless -f;
push @file_list;
}, $source_dir);
for my $file (@file_list) {
...
}
Это только выглядит лучше. Я вижу, что @file_list
манипулирует непосредственно моей командой find
. Плюс, что надоедливый хотел подпрограмма исчезла с конца моей программы. Его "тот же самый код. Это выглядит лучше.
Давайте к тому, что команда find
делает и как он работает с wanted
подпрограммы:
Команда find
находит каждый и каждый файл, каталог, ссылки, или этажерку, расположенный в списке каталогов вы переходите к нему.С каждым предметом он находит в этом каталоге, он передает его на ваш хотел подпрограмма для обработки. A return
оставляет запрошенной подпрограммой и позволяет find
получить следующий товар.
Каждый раз хотел подпрограммой называется, find
устанавливает три переменные:
$File::Find::name
: Имя элемента найден полный путь прилагается к нему.
$File::Find::dir
: Название каталога, в котором был найден элемент.
$_
: Название элемента без имени каталога.
В Perl, что $_
переменная очень особенная. Для многих команд это переменная по умолчанию. То есть вы выполняете команду и не используете ее для использования, эта команда будет использовать $_
. Например:
print
распечатывает $_
return if -f;
То же самое, говоря это:
if (-f $_) {
return;
}
Это for
петля:
for (@file_list) {
...
}
То же самое, как это :
for $_ (@file_list) {
...
}
Обычно я избегаю переменной по умолчанию. Он глобальный по своему охвату, и не всегда очевидно, на что воздействует. Тем не менее, есть несколько обстоятельств, где я буду использовать это, потому что на самом деле проясняет смысл программы:
return unless -f;
в моем хотел функции очень очевидна. Я выхожу из запрошенной подпрограммы, если мне не был передан файл. Вот еще:
return unless /\.txt$/;
Это приведет к выходу мой хотел функцию, если элемент не заканчивается «.txt».
Надеюсь, это пояснит, что делает моя программа. Кроме того, я удалил несколько ошибок, пока я был на нем. Я отклонил $File::Find::dir
до $File::Find::name
, поэтому у вас есть ошибка.
Если вы не можете использовать модули CPAN, это не означает, что вы не можете использовать их реализацию. Посмотрите, например, на [Каталог :: Итератор] (http://search.cpan.org/~sanbeg/Directory-Iterator-1.001/lib/Directory/Iterator.pm). Получайте кредит, в котором должен быть предоставлен кредит. –
Домашнее задание или есть еще одна причина, по которой вы не можете использовать 'cp -r'? – flesk
Также см. Https://metacpan.org/module/File::Copy::Recursive для справки. – simbabque