perl中的脚本,用于将目录结构从源复制到目标

时间:2013-05-30 12:53:04

标签: perl

#!/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模块

1 个答案:

答案 0 :(得分: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处理为您复制目录。

顺便说一句,您可以像documentation那样重写find这就是您通常在{{3}}中看到的内容:

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