我有许多带.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/' {} +
答案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