我尝试给当前目录和所有子目录中所有不是.sh
文件的文件添加前缀bash
using find
。
问题是我得到了一个错误:
mv: cannot move './test.txt' to 'PRE_./test.txt': No such file or directory
我的一句俏皮话是:
find -type f ! -name "*.sh" -exec sh -c 'mv "{}" PRE_"{}"' _ {} \;
我也尝试了xargs
和rename
:
find -type f ! -name "*.sh" -print0 | xargs -0 rename 's/(.*)$/PRE_$1/'
但我也犯了同样的错误。怎么了?先谢了!
5条答案
按热度按时间pprl5pva1#
find
返回从.
到文件的路径,因此在当前目录中开始搜索时,每个find
结果都以./
开头。因此,新文件名为PRE_./test.txt
,即PRE_.
目录中的test.txt
。由于不存在PRE_.
目录,mv
无法将test.txt
移动到该目录中,因此会显示您看到的错误消息。如果您只重命名当前目录中的文件,而不是子目录中的文件,则可以使用bash:
感谢bash中
!(pattern)
结构的this answer,当extglob
启用时可用,它返回所有与pattern
不匹配的文件名。对于所有子目录中的文件,也用途:
多亏了globstar的this answer,
%
和##
将路径组件截断,并将PRE_
粘贴到正确的位置,即文件名的开头。编辑上述两个选项中的
[[ -d "$f" ]] ||
可防止循环重命名目录。8xiog9wr2#
最好不要在
sh -c
的参数中直接使用{}
,而是将其值作为参数传入。然后,您可以像往常一样使用参数扩展操作来修改其值。这里,我们将把参数拆分为目录和文件名组件,然后将它们重新组合,包括文件名前缀PRE_
,以用作mv
的参数。(This只是你最初尝试的一个小小的改变你传递了
{}
作为参数,但没有使用它,而是将值直接“嵌入”到传递给sh
的字符串中。)您还可以使用
-exec +
表单来最小化对sh
的调用次数,方法是传递多个文件名并在shell中迭代它们。(这开始与@cxw建议的纯bash
解决方案收敛,但保持POSIX以及bash
3.x的兼容性):pu3pd22g3#
如果您不想更改子目录中的文件,可以使用以下命令:
问题是文件名前面的./。
vxqlmq5t4#
.*/
匹配到文件名中的最后一个/
\K
lookbehind,在这种情况下我们可以避免捕获组的需要显示正确重命名后,从
rename
中删除-n
选项w6lpcovy5#
在许多Linux发行版中,rename命令默认不可用。如果您的系统缺少rename命令,请使用以下命令安装:
对于Ubuntu和Debian,使用sudo apt安装重命名
对于CentOS和Fedora,使用sudo yum安装预命名