将文件重命名为大写但不影响文件扩展名

将文件重命名为大写但不影响文件扩展名

我在客户的目录中使用它来重命名文件,并根据他们的要求将每个单词的首字母大写:

rename 's/\b(\w)/\u$1/g' *

这可以正常工作,但是它也会将扩展名的首字母大写,因此我使用它来解决这个问题:

rename 's/\.Txt$/.txt/' *.Txt

如果文件夹中的大多数文件都具有相同的扩展名(通常如此),则此方法可以正常工作,但如果文件混合在一起,则会很麻烦。

为了解决这个问题,我创建了一个如下所示的小脚本(不要笑!):

#!/bin/bash
rename 's/\.Txt$/.txt/' *.Txt
rename 's/\.Doc$/.doc/' *.Doc
rename 's/\.Docx$/.docx/' *.Docx
...
rename 's/\.Xlsx$/.xlsx/' *.Xlsx

我忽略了“无法重命名 *.Txt *.txt:没有此文件或目录”错误,如果我发现缺少扩展名,我只需将该行添加到我的脚本中。

我认为这无关紧要,但这些文件位于 Windows 服务器文件共享中,而我使用 Ubuntu 16.04LTS 访问它。如果这确实有关系,那么我可以先将它们复制到本地驱动器,在 Ubuntu 中运行命令,然后根据需要将文件移回。

有没有办法修改第一个重命名命令以忽略扩展名并将其保留为小写?我可以在更高级别的目录中运行新命令,并让它遍历所有子目录吗?

答案1

实现此目的的一种方法可能是使用负面后视仅当单词边界 - 单词字符序列前面没有文字句点时才匹配,例如给定

$ ls
alan's file.txt  bar.txt  foo

然后

$ rename -n 's/(?<!\.)\b\w*/\u$&/g' *
rename(alan's file.txt, Alan'S File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)

假设你也想避免将复数首字母大写s,我们可以将其修改为

$ rename -n 's/(?<![.'\''])\b\w*/\u$&/g' *
rename(alan's file.txt, Alan's File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)

甚至

$ rename -n 's/(?<![[:punct:]])\b\w*/\u$&/g' *
rename(alan's file.txt, Alan's File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)

答案2

这将使除最后一个扩展名之外的每个单词的首字母大写:

rename -n 's/\b(.+?)\b/\u$1/g; s/(.+)\.(.)/$1\.\l$2/' *

我使用这些文件进行了测试:

$ ls -1
'a file with a '$'\n''new line.foo'
'a file with spaces.txt'
justonelongfilename.ext
no-extensions-here
this.has.many.extensions.pdf

并将其重命名为:

$ rename -n 's/\b(.+?)\b/\u$1/g; s/(.+)\.(.)/$1\.\l$2/' *
a file with a 
new line.foo -> A File With A 
New Line.foo
a file with spaces.txt -> A File With Spaces.txt
justonelongfilename.ext -> Justonelongfilename.ext
no-extensions-here -> No-Extensions-Here
this.has.many.extensions.pdf -> This.Has.Many.Extensions.pdf

诀窍是先将每个首字母大写,忽略扩展名,然后再返回并将扩展名改为小写:

  • s/\b(.+?)\b/\u$1/g;.+?是一个非贪婪模式,这意味着它将找到最短的匹配。由于它由单词边界(\b)固定,因此它将找到所有单词(全部因为最后的g)。然后将它们替换为大写(首字母大写)版本的单词(\l$1)。

  • s/(.+)\.(.)/$1\.\l$2/.+是贪婪的,所以它会找到最长的匹配。这意味着最长的字符串直到最后的,之后是扩展名(如果有)。我们用扩展名( )、a和首字母再次小写的扩展名( ).之前的所有内容替换匹配。$1.\l$2


递归也很简单。如果你的 shell 是 bash(如果你不知道,可能就是),你可以使用 globstar 选项,匹配**0 个或更多子目录:

shopt -s globstar

现在,像这样运行重命名命令(这也适用于当前目录下的任何文件):

rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' **

要将其限制为仅具有扩展名的文件或目录,请使用**/*.*而不是**

或者,使用find

find /path/to/dir -exec rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' {} +

并且,限制那些具有扩展名的文件和目录:

find /path/to/dir -name '*.*' -exec rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' {} +

注意所有这些解决方案都会愉快地重命名目录和文件。如果您不想这样,请小心告诉它递归的位置,或者给它一个更具体的模式,例如*.txt


在所有示例中,删除 -n 以使它们真正执行某些操作。-n导致它rename只是打印要执行的操作,而实际上不执行任何操作。对测试有用。

答案3

最明智的方式是处理所有“单词”(使用单词边界\b并通过$1任何第一个字符引用),包括扩展名,然后将扩展名本身小写:

$ prename -nv 's/\b(.)/\u$1/g;s/^(.*)\.(.*)/$1.\l$2/' *                                                                                                                
another filename trispaced.txt renamed as Another Filename Trispaced.txt
filename_with_underschore.txt renamed as Filename_with_underschore.txt
one filename.txt renamed as One Filename.txt

请注意,这不适用于带有下划线的文件名,即“单词”边界被视为空白(制表符、空格、换行符 - 希望它们无论如何都不应该出现在文件名中)。

prename请注意,为了实现可移植性,使用ksh,其中rename实际上是一个内置命令,而不是独立的perl可执行文件。

答案4

只需使用rename 's/\b(\w)/\u$1/' *不带 g 标志的标志即可。g
标志表示“全局 = 多次执行”。您第一次在文件名中执行,并且您不想在第二次执行时将其变为大写,对吗?

相关内容