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

tom*_*myk 82 command-line bash rename batch-rename

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

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

gei*_*rha 105

一种可移植的方式(适用于任何 POSIX 兼容系统):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;
Run Code Online (Sandbox Code Playgroud)

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

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done
Run Code Online (Sandbox Code Playgroud)

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/' {} +
Run Code Online (Sandbox Code Playgroud)

另见http://mywiki.wooledge.org/BashFAQ/030

  • @ user2757729,它确实取代了它。`"${1%.abc}"` 扩展为 `$1` 的值,除非末尾没有 `.abc`。有关 shell 字符串操作的更多信息,请参见 [faq 100](http://mywiki.wooledge.org/BashFAQ/100)。 (4认同)
  • @user2757729 您可能在 `${1%.abc}...` 中留下了“abc” (2认同)
  • 如果其他人想知道第一个命令中的下划线在做什么,这是必要的,因为使用 `sh -c` _第一个参数被分配给 $0,而任何剩余的参数被分配给位置参数_。所以 `_` 是一个虚拟参数,而 '{}' 是 `sh -c` 的第一个位置参数。 (2认同)

Cha*_*kra 53

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

rename 's/.abc$/.edefg/' *.abc
Run Code Online (Sandbox Code Playgroud)

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

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
Run Code Online (Sandbox Code Playgroud)

  • 或者在现代版本的 Bash 中`rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abc`。 (8认同)

Chr*_*ong 14

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

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

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

详细:

  • -type f 表示只查找文件,不查找目录
  • -name '*.abc' 表示只匹配以 .abc 结尾的文件名
  • '{}'是标记-execdir将插入找到的路径的位置的占位符。单引号是必需的,以允许它处理带有空格和 shell 字符的文件名。
  • -type和之后的反斜杠-name是 bash 行继续符。我使用它们使这个示例更具可读性,但如果您将命令全部放在一行中,则不需要它们。
  • 但是,-execdir行尾的反斜杠是必需的。它用于转义分号,分号终止由-execdir. 乐趣!

正则表达式的解释:

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

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

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

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

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

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc
Run Code Online (Sandbox Code Playgroud)

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


aNe*_*ino 5

另一种便携方式:

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

  • 欢迎询问 Ubuntu!虽然这是一个有价值的答案,但我建议扩展它(通过编辑)以解释该命令*如何以及为什么*起作用。 (4认同)

小智 5

# Rename all *.txt to *.text
for f in *.txt; do 
mv -- "$f" "${f%.txt}.text"
done
Run Code Online (Sandbox Code Playgroud)

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

编辑:如果您必须使用基本名称,您的语法将是:

for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done
Run Code Online (Sandbox Code Playgroud)

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