如何使用find重命名多个文件

use*_*014 61 shell find

我想使用以下find命令重命名多个文件(file1 ... fileN 到 file1_renamed ... fileN_renamed):

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'
Run Code Online (Sandbox Code Playgroud)

但收到此错误:

mv: cannot stat ‘filename=./file1’: No such file or directory
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为文件名不被解释为 shell 变量。

Tho*_*ker 70

以下是您的方法的直接修复:

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;
Run Code Online (Sandbox Code Playgroud)

但是,如果您有很多匹配的文件,这是非常昂贵的,因为您会mv为每个匹配启动一个新的 shell(执行 a )。如果任何文件名中有有趣的字符,这会爆炸。一种更有效和安全的方法是:

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed
Run Code Online (Sandbox Code Playgroud)

它还具有处理奇怪命名文件的好处。如果find支持它,这可以减少到

find . -type f -name 'file*' -exec mv {} {}_renamed \;
Run Code Online (Sandbox Code Playgroud)

xargs版本在不使用时很有用{},如

find .... -print0 | xargs --null rm
Run Code Online (Sandbox Code Playgroud)

这里rm被调用一次(或多次调用大量文件),但不是每个文件。

我删除了basename你的问题,因为它可能是错误的:你会移动foo/bar/file8file8_renamed,而不是foo/bar/file8_renamed

编辑(如评论中所建议):

  • 添加缩短find没有xargs
  • 添加了安全贴纸

  • (1) 在 shell (`sh -c "..."`) 命令中直接使用 `{}` 是非常危险的——你应该总是将它作为参数传入。(2) 并非所有版本的 `find` 都支持 `{}_renamed` 构造。(3) 我不明白你说 `xargs` 对删除文件很有用(与重命名它们相反)。 (3认同)

Mat*_*ijs 21

在尝试了第一个答案并稍微玩弄它之后,我发现它可以使用以下方法稍微缩短且不那么复杂-execdir

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'
Run Code Online (Sandbox Code Playgroud)

看起来它也应该完全满足您的需求。

  • 使用不作为一个整体支持 `-execdir` 和 `{}` 的 `find` 实现,它也是最安全的。你可能想在 `mv` 中添加一个 `-i`(如果你的 `mv` 支持的话,还有 `-T`) (2认同)

Joh*_*n B 9

另一种方法是while readfind输出上使用循环。这允许将每个文件名作为可以操作的变量进行访问,而不必担心sh -c使用find's-exec选项生成单独进程的额外成本/潜在安全问题。

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done
Run Code Online (Sandbox Code Playgroud)

如果正在使用的 shell 支持-d指定read分隔符的选项,您可以使用以下命令支持奇怪命名的文件(例如带有换行符):

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done
Run Code Online (Sandbox Code Playgroud)


小智 7

我想扩展第一个答案,并注意这将无法附加到文件名,因为./路径前缀存在于文件名参数中。

修改 Thomas Erker 的答案,我发现这是一种更通用的方法

find . -name PATTERN -printf "%f\0" | xargs --null -I{} mv {} "prefix {} suffix"
Run Code Online (Sandbox Code Playgroud)

xargs 选项:

--null指示传递的每个参数stdin均以空字符 ( \0) 结尾。这样,文件名可以包含空格,否则每个单词都将作为命令的不同参数受到威胁mv

-I replace-str每次出现replace-str都会被替换为从 读取的参数stdin。因此,如果需要,您可以将其更改为其他字符串。

  • @sim,因为如果“PATTERN”包含 shell 元字符,则“-print0”将生成“./”前缀,这些字符在重命名为原始名称前缀的内容时会妨碍。(例如,将“0 - foo.txt”重命名为“00 - foo.txt”,将“1 - bar.txt”重命名为“01 - bar.txt”等) (2认同)

use*_*194 7

我认为如果在这里提供重命名文件但仅更改其名称的一部分的选项,那就太好了。例如文件的扩展名。

如果您只能在文件名中添加一些内容,那么更改文件名有什么意义呢?

find . -name "*.txt" -exec  sh -c 'x={}; mv "$x" $(echo $x | sed 's/\.txt/\.bat/g')' \;
Run Code Online (Sandbox Code Playgroud)


小智 6

我能够做的类似的东西forfindmv

for i in $(find . -name 'config.yml'); do mv $i $i.bak; done
Run Code Online (Sandbox Code Playgroud)

这将找到所有config.yml文件并将它们重命名为config.yml.bak

  • 如果文件名包含空格,则此解决方案不起作用。 (2认同)