为什么不在 Perl 中做所有的事情呢?
#! /usr/bin/env perl
use 5.12.0;
use warnings;
use autodie;
use File::Find;
use Getopt::Long;
在这里稍作休息...
File::Find是一个标准的Perl 模块,它允许您在 Perl 内部进行文件查找。Getopt::Long是一个很好的小命令,可以为您解析命令行参数。这两者都是 Perl 标准包的一部分,绝对没有理由不使用它们。
现在,回到我们正在进行的程序。
my ($from, $to, $dir);
my $usage =<<USAGE;
find_and_rename.pl -from <from> -to <to> -dir <dir>
USAGE
GetOptions {
'from=s' => $from,
'to=s' => $to,
'dir=s' => $dir,
} or die ($usage\n);
if ( not $from or not $to or not $dir ) {
die ($usage\n);
}
另一个休息... 看看如何很好地Getopt::Long解析命令行?一个函数GetOptions为您完成所有解析。您需要做的就是验证输入。(而且,GetOptions 甚至可以为您做到这一点!)。查看Perldoc并查看几乎所有 Perl 安装中包含的各种模块。它们和 Perl 语言一样,就像popor之类的命令一样push,这些模块可以使 Perl 编程变得更加容易。
my @files_found;
find ( sub {
return unless /$from/;
@files_found, $File::Find::name;
}, $dir);
for my $file ( @files_found ) {
(here be dragons...)
rename $file, $to_name;
}
这是您的程序的粗略大纲。这一切都取决于您真正想要完成的任务,但它确实显示了如何在单个程序中完成所有事情,而不是依赖使用 find,然后将 Perl 脚本嵌入到 find 中。所有这些都不需要额外的工作。
您可能想要添加更多功能(例如告诉您每个文件重命名为的内容,并且可能包含一个-dry-run选项,您可以使用该选项来查看结果的外观而无需重命名任何内容。
我更喜欢用它find来填写我以后可以操作的文件名列表。没有理由不能将其包含在查找中。我也喜欢将我的子程序直接嵌入到我的find命令中。然而:
find ( sub {
return unless /$from/;
@files_found, $File::Find::name;
}, $dir);
相当于:
find ( \&wanted, $dir);
sub wanted {
return unless /$from/;
@files_found, $File::Find::name;
}
这可能会让你更容易理解。
要使用您的程序,您只需执行以下操作:
$ find_and_replace.pl -dir . -from REPLACE_THIS -to with_this
现在有一个特别的奖金优惠!
把最好的留到最后。有一个命令find2perl可以将您的 Unix Find 命令转换为 Perl 脚本。而且,最好的部分:它已经在您的系统上:
$ find2perl find /tmp -name "*$NAME_THAT_WE_WANT_TO_CHANGE*" -exec /tmp/rename.pl \;
#! /usr/bin/perl -w
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
if 0; #$running_under_some_shell
use strict;
use File::Find ();
# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.
# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name = *File::Find::name;
*dir = *File::Find::dir;
*prune = *File::Find::prune;
sub wanted;
sub doexec ($@);
use Cwd ();
my $cwd = Cwd::cwd();
# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, 'find', '/tmp');
exit;
sub wanted {
/^.*.*\z/s &&
doexec(0, '/tmp/rename.pl');
}
sub doexec ($@) {
my $ok = shift;
my @command = @_; # copy so we don't try to s/// aliases to constants
for my $word (@command)
{ $word =~ s#{}#$name#g }
if ($ok) {
my $old = select(STDOUT);
$| = 1;
print "@command";
select($old);
return 0 unless <STDIN> =~ /^y/;
}
chdir $cwd; #sigh
system @command;
chdir $File::Find::dir;
return !$?;
}
由于您doexec只是一个 Perl 脚本,因此您可以用您的 Perl 代码替换整个子例程。