如何从命令行递归更改多个文件的扩展名?


Answers:


85

可移植的方式(可在任何POSIX兼容系统上使用):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

在bash4中,可以使用globstar获取递归glob(**):

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

renameUbuntu中的(perl)命令可以使用perl正则表达式语法重命名文件,您可以将其与globstar或组合使用find

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

另请参阅http://mywiki.wooledge.org/BashFAQ/030


第一个只是将新扩展名附加到旧扩展名上,而不是替换...
user2757729 2014年

3
@ user2757729,它确实取代了它。"${1%.abc}"扩展为$1except 的值,不带.abc末尾。有关shell字符串操作的更多信息,请参见常见问题100
盖尔哈

我在约70k的文件文件结构上运行了它……至少在我的shell上,它绝对没有取代它。
user2757729 2014年

1
@ user2757729你可能留在“ABC”${1%.abc}...
kvnn

1
如果其他人想知道下划线在第一个命令中的作用,则需要这样做,因为sh -c 将第一个参数分配给$ 0,并将所有其余参数分配给position参数。因此,_是一个伪参数,而'{}'是的第一个位置参数sh -c
hellobenallan,

48

如果所有文件都在同一文件夹中,这将执行所需的任务

rename 's/.abc$/.edefg/' *.abc

要递归重命名文件,请使用以下命令:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'

8
rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abc在现代版本的Bash中。
亚当·伯特

非常感谢亚当给我有关如何递归在文件夹中使用* .abc的提示!
2011年

大提示,谢谢!在哪里可以找到有关正则表达式小语法的更多文档?就像s刚开始的时候一样,在那里我还可以使用其他选项。谢谢 !
2013年

@AdamByrtek我只是对您关于Utopic的建议失败。(“没有此类文件”或某些此类文件。)
乔纳森·Y.2015年

14

递归重命名的一个问题是,无论您使用什么方法查找文件,它都会将整个路径传递给rename,而不仅仅是文件名。这使得很难在嵌套文件夹中执行复杂的重命名。

我使用find-execdir动作来解决此问题。如果使用-execdir代替-exec,则从包含匹配文件的子目录中运行指定的命令。因此,它不会传递整个路径rename,而只会传递./filename。这使得编写正则表达式变得容易得多。

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;

详细:

  • -type f 表示仅查找文件,不查找目录
  • -name '*.abc' 表示仅匹配以.abc结尾的文件名
  • '{}'是占位符,用于标记将在其中-execdir插入找到的路径的位置。必须使用单引号将其引起来,以便使用空格和外壳字符处理文件名。
  • -type和之后的反斜杠-name是bash行连续字符。我使用它们使该示例更具可读性,但是如果将命令全部放在一行上,则不需要它们。
  • 但是,在行的末尾-execdir需要反斜杠。它是用来转义分号的地方,分号将终​​止运行的命令-execdir。好玩!

正则表达式的解释:

  • s/ 正则表达式的开始
  • \.\/匹配-execdir传入的前导./。使用\来使。逸出。和/元字符(请注意:这部分取决于您的的版本find。请参阅用户@apollo的评论)
  • (.+)匹配文件名。括号捕获了该匹配项以供以后使用
  • \.abc 转义,匹配abc
  • $ 将匹配项锚定在字符串的末尾

  • / 标记正则表达式“匹配”部分的结尾,以及“替换”部分的开头

  • version1_ 将此文本添加到每个文件名

  • $1引用现有的文件名,因为我们用括号捕获了它。如果在“匹配”部分使用多组括号,则可以在此处使用$ 2,$ 3等引用它们。
  • .abc新文件名将以.abc结尾。无需在“替换”部分中转义点元字符
  • / 正则表达式的结尾

之前

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

提示:rename-n选项非常有用。它会进行试运行,并向您显示它将更改的名称,但不会进行任何更改。


感谢您的详细说明,但是奇怪的是,当我尝试此操作时,--execdir并没有领先,./因此\.\/可以省略正则表达式中的部分。可能是因为我在OSX上不是ubuntu
阿波罗

5

另一种可移植的方式:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;

4
欢迎来到Ask Ubuntu!尽管这是一个有价值的答案,但我建议将其扩展(通过编辑)以说明该命令的工作方式和原因
卡根


0

这就是我所做的,并且按照我想要的方式工作。我使用了mv命令。我有多个.3gp文件,我想将它们全部重命名为.mp4

这是一个简短的清单:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

它只需扫描当前目录,拾取所有.3gp文件,然后使用mv重命名为 ren-name_of_file.mp4


那将重命名file.3gpfile.3gp.mp4,这可能不是大多数人想要的。
muru

@muru,但原理仍然存在。只需选择任何扩展名,然后指定目标扩展名即可将其重命名。的.3gp.mp4用于说明目的这里只是。
KhoPhi

该语法是可取的,是一种语法并且易于理解。但是,我将basename "$i" .mp4删除以前的扩展名而不是“ ren- $ i.mp4”。
TaoPR

真正。好点子。
KhoPhi

0

我找到了一种简单的方法来实现这一目标。要将许多文件的扩展名从jpg更改为pdf,请使用:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done

0

我会用MMV命令包同名

mmv ';*.abc' '#1#2.edefg'

;匹配零个或更多*/,并对应于#1在替换。该*对应#2。非递归版本将是

mmv '*.abc' '#1.edefg'

0

重命名文件和目录 find -execdir | rename

如果您不只是使用后缀来重命名文件和目录,那么这是一个很好的模式:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find . -depth -execdir rename 's/findme/replaceme/' '{}' \;

与相比,真棒-execdir选项cd在执行rename命令之前会对目录执行a 操作-exec

-depth 确保重命名首先在孩子上进行,然后在父母上进行,以防止丢失父目录的潜在问题。

-execdir 是必需的,因为重命名在非基本名称的输入路径中不能很好地发挥作用,例如,以下操作失败:

rename 's/findme/replaceme/g' acc/acc

PATH需要的黑客,因为-execdir有一个很讨厌的缺点:find极其固执己见,拒绝做任何事情-execdir,如果您有任何相对路径在您的PATH环境变量,例如./node_modules/.bin,与失败:

find:相对路径'./node_modules/.bin'包含在PATH环境变量中,与find的-execdir操作结合使用时,它是不安全的。请从$ PATH中删除该条目

另请参阅:为什么对PATH中的目录使用'-execdir'操作不安全?

-execdirPOSIX的GNU查找扩展rename基于Perl,来自rename软件包。在Ubuntu 18.10中测试。

重命名提前解决方法

如果您的输入路径不是来自find,或者您已经受够了相对路径的烦恼,我们可以使用一些Perl预行来安全地重命名目录,如下所示:

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

我还没有找到一个方便的模拟为-execdir具有xargshttps://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686

sort -r要求,以确保文件有其各自的目录后,由于较长的路径都较短的具有相同前缀之后。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.