我想使用find
命令重命名多个文件(file1 ... fileN 到 file1_renamed ... fileN_renamed):
find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'
但出现此错误:
mv: cannot stat ‘filename=./file1’: No such file or directory
这不起作用,因为文件名不被解释为 shell 变量。
答案1
以下是您的方法的直接修复:
find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;
但是,如果您有大量匹配文件,则这是非常昂贵的,因为您mv
为每个匹配启动一个新的 shell(执行 a )。如果任何文件名中包含有趣的字符,就会爆炸。更有效、更安全的方法是:
find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed
它还具有处理名称奇怪的文件的好处。如果find
支持的话,这可以减少到
find . -type f -name 'file*' -exec mv {} {}_renamed \;
该xargs
版本在不使用时很有用{}
,如
find .... -print0 | xargs --null rm
这里rm
被调用一次(或者对于很多文件多次),但不是每个文件。
我删除了basename
您的问题,因为它可能是错误的:您会移至foo/bar/file8
,file8_renamed
而不是foo/bar/file8_renamed
。
编辑(如评论中建议的):
- 添加缩短
find
无xargs
- 添加了安全贴纸
答案2
尝试后第一个答案稍微玩弄一下,我发现它可以使用以下命令来稍微缩短和简化-execdir
:
find . -type f -name 'file*' -execdir mv {} {}_renamed ';'
看起来它也应该完全满足您的需要。
答案3
另一种方法是使用while read
循环输出find
。这允许将每个文件名作为可以操纵的变量进行访问,而不必担心sh -c
使用find
's-exec
选项生成单独进程的额外成本/潜在安全问题。
find . -type f -name 'file*' |
while IFS= read file_name; do
mv "$file_name" "${file_name##*\/}_renamed"
done
如果正在使用的 shell 支持-d
指定read
分隔符的选项,您可以使用以下命令支持奇怪命名的文件(例如,使用换行符):
find . -type f -name 'file*' -print0 |
while IFS= read -d '' file_name; do
mv "$file_name" "${file_name##*\/}_renamed"
done
答案4
我能够用 、 和 做类似for
的find
事情mv
。
for i in $(find . -name 'config.yml'); do mv $i $i.bak; done
这会找到所有config.yml
文件并将它们重命名为config.yml.bak