使用Perl实现linux命令'rename'

时间:2013-02-03 20:08:04

标签: linux macos perl

Mac Os X没有有用的linux命令rename,它具有以下格式:

rename 'perl-regex' list-of-files

所以这就是我放在一起但它没有重命名任何文件($ new总是与$ file相同):

#!/usr/bin/env perl -w
use strict;
use File::Copy 'move';

my $regex=shift;
my @files=@ARGV;

for my $file (@files)
{
    my $new=$file;
    $new =~ "$regex";    # this is were the problem is !!!
    if ($new ne $file) 
    {
        print STDOUT "$file --> $new \n";
        move $file, ${new} or warn "Could not rename $file to $new";
    }
}

就好像我没有通过正则表达式,如果我将其硬编码为

$new =~ s/TMP/tmp;

它会工作得很好...... 有什么想法吗?

4 个答案:

答案 0 :(得分:3)

$operator = 's/TMP/tmp/';
print $operator; 

并没有神奇地评估操作员,所以

应该不足为奇
$operator = 's/TMP/tmp/';
$x =~ $operator; 

也没有。如果要评估Perl代码,则必须将其传递给Perl解释器。您可以使用eval EXPR访问它。

$operator = 's/TMP/tmp/';
eval('$x =~ '.$operator.'; 1')
   or die $@;

答案 1 :(得分:2)

您不能将整个句子s/TMP/tmp;放在变量中。但是,您可以执行类似

的操作
$new =~ s/$find/$replace;

$find是你的正则表达式,$replace你要用什么代替匹配。

如果您仍想传递整个句子,可能需要查看eval()

答案 2 :(得分:2)

有两种方法可以优雅地解决这个问题

  1. 需要两个单独的命令行参数:一个用于正则表达式,另一个用于替换。这是不优雅和限制性的。

    my ($search, $replace, @files) = @ARGV;
    
    ...;
    
    my $new = $file;
    $new =~ s/$search/$replace/e; # the /e evals the replacement,
                                  # allowing us to interpolate vars
    

    像[{1}}一样调用。这允许通过字符串变量插值执行几乎任何代码。

    (1):旧的perls中没有嵌套的正则表达式

  2. 只允许任意Perl代码,类似于my-rename '(.*)\.txt' '@{[our $i++]}-$1.foo' *.txtperl -ne'...'开关的语义是当前行作为-n传递。将文件名作为$_传递是有意义的,并使用最后一个语句的值作为新文件名。这会导致像

    这样的东西
    $_

    这可以像# somewhat tested my ($eval_content, @files) = @ARGV; my $code = eval q' sub { no strict; # could be helpful ;-) my @_out_; FILENAME: for (@_) { my $_orig_ = $_; push @_out_, [ $_orig_ => do { ' . $eval_content . q' } ]; # or # do { " . $eval_content . " }; # push @_out_, [ $_orig_, $_ ]; # if you want to use $_ as out-argument (like -p). # Can lead to more concise code. } return @_out_; } '; die "Eval error: $@" if $@; for my $rename ($code->(@files)) { my ($from, $to) = @$rename; ... } 一样调用。跳过以点开头的所有文件,注册一个全局变量my-rename 'next FILENAME if /^\./; our $i++; s/(.*)\.txt/$i-$1.foo/; $_' *.txt,并在每个文件名前面放一个向上计数的数字,并更改扩展名。然后我们在最后一个语句中返回$i

    循环构建原始文件名和新文件名的对,可以在第二个循环中处理。

    这可能非常灵活,而且效率不高。

答案 3 :(得分:2)

嗯,它已经是一个Perl实用程序了,它在CPAN上:http://cpan.me/rename。您可以直接使用该实用程序附带的模块File::Rename

#!/usr/bin/env perl
use File::Rename qw(rename);
rename @ARGV, sub { s/TMP/tmp/ }, 'verbose';

其他可能性是从该分发中连接模块和脚本,并将生成的文件放在$PATH的某个位置。