0
#!/usr/bin/perl -w
use    File::Copy;
use    strict;

my   $i=  "0";
my   $j=  "1";
my   $source_directory = $ARGV[$i]; 
my    $target_directory = $ARGV[$j];
#print $source_directory,"\n";
#print $target_directory,"\n";

my@list=process_files ($source_directory);
print "remaninign files\n";
print @list;
# Accepts one argument: the full path to a directory.
# Returns: A list of files that reside in that path.
sub process_files {
    my $path = shift;

    opendir (DIR, $path)
     or die "Unable to open $path: $!";

    # We are just chaining the grep and map from
    # the previous example.
    # You'll see this often, so pay attention ;)
    # This is the same as:
    # LIST = map(EXP, grep(EXP, readdir()))
    my @files = 
        # Third: Prepend the full path
        map { $path . '/' . $_}
        # Second: take out '.' and '..'
        grep { !/^\.{1,2}$/ }
        # First: get all files
        readdir (DIR);

    closedir (DIR);

    for (@files) {
        if (-d $_) {
            # Add all of the new files from this directory
            # (and its subdirectories, and so on... if any)
            push @files, process_files ($_);

        } else { #print @files,"\n";
          # for(@files)
        while(@files) 
        {  
        my $input= pop @files;
        print $input,"\n";
        copy($input,$target_directory);

             }
  }
      # NOTE: we're returning the list of files
   return @files;
  }
}

这基本上将文件从源复制到目标,但我还需要一些关于如何复制目录的指导。这里要注意的主要事情是除了复制、移动和路径之外,不允许使用 CPAN 模块

4

1 回答 1

2

与其滚动您自己的目录处理冒险,不如简单地使用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像这样重写,这就是您通常在文档中看到的方式:

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命令查找您传递给它的目录列表中的每个文件、目录、链接或其他内容。对于在该目录中找到的每个项目,它会将其传递给您想要的子例程进行处理。Areturn离开想要的子程序并允许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这就是您收到错误的原因。

于 2013-05-30T14:12:23.133 回答