我想使用以下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/file8到file8_renamed,而不是foo/bar/file8_renamed。
编辑(如评论中所建议):
find没有xargsMat*_*ijs 21
在尝试了第一个答案并稍微玩弄它之后,我发现它可以使用以下方法稍微缩短且不那么复杂-execdir:
find . -type f -name 'file*' -execdir mv {} {}_renamed ';'
Run Code Online (Sandbox Code Playgroud)
看起来它也应该完全满足您的需求。
另一种方法是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
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。因此,如果需要,您可以将其更改为其他字符串。
我认为如果在这里提供重命名文件但仅更改其名称的一部分的选项,那就太好了。例如文件的扩展名。
如果您只能在文件名中添加一些内容,那么更改文件名有什么意义呢?
find . -name "*.txt" -exec sh -c 'x={}; mv "$x" $(echo $x | sed 's/\.txt/\.bat/g')' \;
Run Code Online (Sandbox Code Playgroud)
小智 6
我能够做的类似的东西for,find和mv。
for i in $(find . -name 'config.yml'); do mv $i $i.bak; done
Run Code Online (Sandbox Code Playgroud)
这将找到所有config.yml文件并将它们重命名为config.yml.bak