【问题标题】:How can I change the case of filenames in Perl?如何在 Perl 中更改文件名的大小写?
【发布时间】:2009-03-24 15:50:25
【问题描述】:

我正在尝试创建一个将我的所有文件名重命名为 Camel/Capital Case 的进程。我必须到达那里最接近的是:

perl -i.bak -ple 's/\b([a-z])/\u$1/g;' *.txt # or similar .extension.

这似乎创建了一个备份文件(我会在验证这符合我的要求后将其删除);但不是重命名文件,而是重命名文件内的文本。有没有更简单的方法来做到这一点?理论是我有几个不同格式的办公文件,因为我有点固执,希望它们看起来像这样:

新文档.odt 罗弗勒.ogg 等等等等 鲍勃·卡特.flac 猫狗.avi

这可以通过 perl 实现,还是我需要更改为另一种语言/它们的组合?

另外,有没有办法让这个递归,这样 /foo/foo/documents 重命名所有文件,就像 /foo/foo/documents/foo 一样?

【问题讨论】:

  • 您没有指定您当前的表格是什么。

标签: perl filenames


【解决方案1】:

您需要使用rename。

这是它的签名:


rename OLDNAME,NEWNAME

要使其递归,请将其与 File::Find

一起使用

use strict;
use warnings;
use File::Basename;
use File::Find;

#default searches just in current directory
my @directories = (".");

find(\&wanted,  @directories);

sub wanted {
  #renaming goes here
}

下面的 sn-p,将对找到的所有文件执行 wanted 内的代码。你必须完成想要里面的一些代码来做你想做的事情。

编辑:我尝试使用 File::Find 完成此任务,但我认为您无法轻松完成它。您可以按照以下步骤成功:

  • 如果参数是dir,则大写,获取所有文件

  • 对于每个文件,如果是目录,则以该文件作为参数返回开头

  • 如果文件是普通文件,则大写

Perl 在编写此脚本时妨碍了我。我用 ruby​​ 写了这个脚本:


require "rubygems"
require "ruby-debug"

# camelcase files

class File
    class << self
        alias :old_rename :rename
    end

    def self.rename(arg1,arg2)
        puts "called with #{arg1} and #{arg2}"
        self.old_rename(arg1,arg2)
    end
end

def capitalize_dir_and_get_files(dir)
    if File.directory?(dir)
        path_c          = dir.split(/\//)
        #base           = path_c[0,path_c.size-1].join("/")
        path_c[-1].capitalize!  
        new_dir_name    = path_c.join("/")
        File.rename(dir,new_dir_name)
        files = Dir.entries(new_dir_name) - [".",".."]
        files.map! {|file| File.join(new_dir_name,file)}
        return files 
    end
    return []
end

def camelize(dir)
    files = capitalize_dir_and_get_files(dir)
    files.each do |file|
        if File.directory?(file)
            camelize(file.clone)
        else
            dir_name    = File.dirname(file)
            file_name   = File.basename(file)
            extname     = File.extname(file)
            file_components = file_name.split(/\s+/)
            file_components.map! {|file_component| file_component.capitalize}           
            new_file_name = File.join(dir_name,file_components.join(" "))
            #if extname != ""
            #   new_file_name += extname
            #end
            File.rename(file,new_file_name)
        end
    end
end

camelize(ARGV[0])

我在我的电脑上尝试了这个脚本,它按照你提到的规则将所有目录、子目录和文件大写。我认为这是您想要的行为。很抱歉没有提供 perl 版本。

【讨论】:

  • search.cpan.org/dist/File-Find-Rule/lib/File/Find/Rule.pm 比 File::Find 漂亮,但是 YMMV。
  • 我几乎是使用 File::Find 长大的 :)。我更喜欢它。
  • 全局符号“@directories”需要在 bob.pl 第 5 行显示包名。bob.pl 的执行由于编译错误而中止。尝试运行 bob.pl(最新编辑)^ 是结果。
  • perl -MFile::Find -e'finddepth(sub{($a=$_)=~s/\b([a-z])/\u$1/g&amp;&amp;rename$_,$a},@ARGV?@ARGV:".")'
【解决方案2】:

大多数系统都有重命名命令 ....

名字 rename - 重命名多个文件

概要 重命名 [-v] [-n] [-f] perlexpr [文件]

描述 "rename" 重命名根据指定为第一个参数的规则提供的文件名。 perlexpr 参数是一个 Perl 表达式 预计至少会为某些指定的文件名修改 Perl 中的 $_ 字符串。如果给定的文件名没有被表达式修改, 它不会被重命名。如果命令行没有给出文件名,文件名将通过标准输入读取。

   For example, to rename all files matching "*.bak" to strip the extension, you might say

           rename 's/\.bak$//' *.bak

   To translate uppercase names to lower, you’d use

           rename 'y/A-Z/a-z/' *

选项 -v,--详细 详细:打印文件名已成功重命名。

   -n, --no-act
           No Action: show what files would have been renamed.

   -f, --force
           Force: overwrite existing files.

作者 拉里·沃尔

诊断 如果你给出一个无效的 Perl 表达式,你会得到一个语法错误。

【讨论】:

    【解决方案3】:

    由于 Perl 在多个平台上运行良好,我警告您 FAT(和 FAT32 等)文件系统将忽略仅更改文件名大小写的重命名。这在 Windows 和 Linux 下是正确的,对于支持 FAT 文件系统的其他平台可能也是如此。

    因此,除了Geo's answer,请注意您可能必须实际更改文件名(例如,通过在末尾添加一个字符),然后将其更改回您想要的名称,并使用正确的大小写。

    如果您只重命名 NTFS 文件系统或 ext2/3/4 文件系统(或其他 UNIX/Linux 文件系统)上的文件,那么您可能不需要担心这一点。我不知道 Mac OSX 文件系统是如何工作的,但由于它是基于 BSD 的,我认为它只允许您通过更改名称的大小写来重命名文件。

    【讨论】:

    • 与 BSD 文件系统不同的是,OS X 的默认 HFS+ 不区分大小写。我附近没有 Mac 来测试仅大小写重命名是否有效...
    • Mac (10.5) 上的默认 HFS+ 卷上的仅大小写重命名不起作用:“$ touch x && mv x X mv: x' and X' are the same file”跨度>
    【解决方案4】:

    我只是使用 find 命令递归子目录并使用 mv 进行重命名,但仍然利用 Perl 来正确重命名。

    find /foo/foo/documents -type f \
         -execdir bash -c 'mv "$0" \
                              "$(echo "$0" \
                                  | perl -pe "s/\b([[:lower:]])/\u\$1/g; \
                                              s/\.(\w+)$/.\l\$1/;")"' \
                  {} \;
    

    神秘,但它有效。

    【讨论】:

    • StackOverflow 不允许 cmets 少于 10 个字符。我只想说:+1,极客。
    • 不好意思写了。
    【解决方案5】:

    另一个:

    find . -type f -exec perl -e'
      map {
            ( $p, $n, $s ) = m|(.*/)([^/]*)(\.[^.]*)$|;
            $n =~ s/(\w+)/ucfirst($1)/ge;
            rename $_, $p . $n . $s;
          } @ARGV
      ' {} +
    

    【讨论】:

      【解决方案6】:

      请记住,在记住大小写的文件系统 (FAT/NTFS) 上,您需要先将文件重命名为其他名称,然后再更改大小写。从“etc.etc”直接重命名为“Etc.Etc”将失败或被忽略,因此您需要进行两次重命名:“etc.etc”为“etc.etc~”,然后为“etc.etc~”以“Etc.Etc”为例。

      【讨论】:

        猜你喜欢
        • 2012-05-18
        • 2011-12-02
        • 2016-09-12
        • 1970-01-01
        • 2011-03-17
        • 1970-01-01
        • 1970-01-01
        • 2016-06-08
        相关资源
        最近更新 更多