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

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

我有许多带.abc扩展名的文件,想将它们更改为.edefg
如何从命令行执行此操作?

我有一个带有许多子文件夹的根文件夹,因此解决方案应该以递归方式工作。

答案1

一种可移植的方式(可在任何符合 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

Ubuntu 中的(perl)rename命令可以使用 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

答案2

如果所有文件都在同一个文件夹中,这将完成所需的任务

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

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

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

答案3

递归重命名的一个问题是,无论您使用哪种方法来定位文件,它都会将整个路径传递给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。单引号是必需的,以便它能够处理带有空格和 shell 字符的文件名。
  • -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 选项很有用。它会进行试运行并向您显示将更改哪些名称,但不会进行任何更改。

答案4

# Rename all *.txt to *.text
for f in *.txt; do 
mv -- "$f" "${f%.txt}.text"
done

另请参阅有关为什么不应该解析的条目ls

编辑:如果必须使用 basename,则语法将是:

for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done

https://unix.stackexchange.com/questions/19654/changing-extension-to-multiple-files

相关内容