0

我有一个重命名文件/目录名称的 perl 行代码

代码:

find /tmp -name "*$NAME_THAT_WE_WANT_TO_CHANGE*" -exec /tmp/rename.pl  's/\Q$ENV{NAME_THAT_WE_WANT_TO_CHANGE}\E/$1$ENV{NEW_NAME}$2/' {} +

我想在我的代码中添加打印消息(警告),以便打印将更改的文件/目录

所以我添加了这一行:

       && warn "Rename file - [$ENV{$NAME_THAT_WE_WANT_TO_CHANGE}"'

所以最后更新代码将是:

find /tmp -name "*$NAME_THAT_WE_WANT_TO_CHANGE*" -exec /tmp/rename.pl  's/\Q$ENV{NAME_THAT_WE_WANT_TO_CHANGE}\E/$1$ENV{NEW_NAME}$2/ {} + && warn "Rename file - [$ENV{$NAME_THAT_WE_WANT_TO_CHANGE}"'

当我运行 perl one liner 时,我收到以下错误消息:

    find: missing argument to `-exec'

请建议我需要在我的代码中修复/更新什么?

. . . . .

完整示例(在我添加警告“.........”之前)

cd /tmp
touch orig-name
touch new-name
export NAME_THAT_WE_WANT_TO_CHANGE=orig-name
export NEW_NAME=new-name



find /tmp -name "*$NAME_THAT_WE_WANT_TO_CHANGE*" -exec /tmp/rename.pl  's/\Q$ENV{NAME_THAT_WE_WANT_TO_CHANGE}\E/$1$ENV{NEW_NAME}$2/' {} +

ls | grep new-name
new-name



more /tmp/rename.pl

#!/usr/bin/perl
#
# rename script examples from lwall:
#       rename 's/\.orig$//' *.orig
#       rename 'y/A-Z/a-z/ unless /^Make/' *
#       rename '$_ .= ".bad"' *.f
#       rename 'print "$_: "; s/foo/bar/ if <stdin> =~ /^y/i' *

$op = shift;
for (@ARGV) {
$was = $_;
eval $op;
die $@ if $@;
rename($was,$_) unless $was eq $_;
4

2 回答 2

0

为什么不在 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 代码替换整个子例程。

于 2013-05-03T15:34:08.307 回答
0
#! /usr/bin/perl
use autodie;
for (@ARGV) {
  rename $_, $_ =~ s/\Q$ENV{ORIGNAME}\E/$ENV{DESTNAME}/r
    and warn "Rename file - [$_]\n";
}

或者如果你在 CentOS 上,并且不能使用例如 cPanel 的新 perl:

#! /usr/bin/perl
for (@ARGV) {
  (my $new = $_) =~ s/\Q$ENV{ORIGNAME}\E/$ENV{DESTNAME}/
    and warn "Rename file - [$_]\n";
  rename $_, $new or die "Rename of ``$_'' to ``$new'' failed: $!";
}

或者如果你真的需要任意 Perl:

#! /usr/bin/perl
my $op = shift;
for (@ARGV) {
  my $old = $_;
  eval $op; die $@ if $@;
  next if $old eq $_;
  rename $old, $_ and warn "Rename file - [$old]\n"
}

注意细微的差异。

在前两种情况下,find适当调整您的:

find /tmp -name "*$ORIGNAME*" -exec /tmp/rename.pl {} +

我的环境变量不那么痛苦并不重要。

顺便说一句,您的错误与其说是 Perl 错误,不如说是一个非常明显 find的错误。 find尤其find-exec危险的,哟:让知道他们在做什么的人在你运行它之前看看你的脚本。

于 2013-05-03T11:25:15.900 回答