perl +在perl单线码中添加警告按摩

时间:2013-05-03 08:38:31

标签: linux perl shell ksh

我有一个用于重命名文件/目录名称的线性代码

代码:

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 $_;

2 个答案:

答案 0 :(得分: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是危险的,哟:如果有人知道他们正在做什么,请在运行前查看您的脚本。

答案 1 :(得分: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语言一样多,就像poppush这样的命令,这些模块可以使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代码替换整个子例程。