我有一个目录,其中包含一些包含文件名和目录名的子目录,其中包含一些点:
$ ls -R
.:
dir file.with.dots
./dir:
subdir.with.dots
./dir/subdir.with.dots:
other.file
Run Code Online (Sandbox Code Playgroud)
我真的很难捕获所有这些文件,find因为我不想重命名当前文件夹.及其..本身。
我还阅读了如何在文件名中用下划线替换点,保持扩展名不变,但这并不适合这里,因为主要问题是文件的递归选择。
我试图用find和替换点tr:
find $(pwd) -depth -name '*.*'|while read f; do mv -iv "$f" '"'$(echo $f|tr '.' '_')'"' ; done
Run Code Online (Sandbox Code Playgroud)
但是如果文件的路径和文件本身都包含点,这会产生错误
如何用下划线重命名和替换所有出现的点?
Sté*_*las 10
与zsh:
autoload zmv
zmv '(**/)(*.*)' '$1${2//./_}'
Run Code Online (Sandbox Code Playgroud)
否则,如果您有权访问bash(尽管ksh93或zsh也将这样做),您可以随时执行以下操作:
find . -depth ! -name '.*' -name '*.*' -exec bash -c '
for file do
dir=${file%/*}
base=${file##*/}
mv -i -- "$file" "$dir/${base//./_}"
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
(尽管您会错过由 完成的健全性检查zmv)。
那些(故意)不会重命名隐藏文件。
关键是处理列表深度优先(zmv默认情况下,并使用-depthin find)并且只重命名文件的基本名称。
这样你就可以:
mv -- a.b/c.d a.b/c_d
Run Code Online (Sandbox Code Playgroud)
并随后:
mv -- a.b a_b
Run Code Online (Sandbox Code Playgroud)
还要注意,对于非zmv的解决方案,如果你有一个a.b文件和一个a_b目录在同一个目录下,那么mv -- a.b a_b会很高兴地移动a.b 到 a_b/. 为避免这种情况,如果在 GNU 系统上,您可以将该-T选项添加到mv.
正如我所看到的,您试图编辑答案以使用while read循环。
请注意,您通常应该避免while read循环,即使在这种情况下,无论如何必须每行运行一个命令。如果你真的想使用一个,那么正确地使用它是很棘手的,并且涉及到 ksh/bash/zshisms:
{
find . -depth ! -name '.*' -name '*.*' -exec printf '%s\0' {} + |
while IFS= read -rd '' file; do
dir=${file%/*}
base=${file##*/}
mv -i -- "$file" "$dir/${base//./_}"
done <&3 3<&-
} 3<&0
Run Code Online (Sandbox Code Playgroud)
(如果您的发现支持,您可以替换-exec printf '%s\0' {} +为-print0)。
你需要:
-print0/-exec printf '%s\0' {} +因为您不能依赖换行符作为分隔符,因为换行符是文件名中的有效字符-d ''(read读取直到 NUL 而不是 NL)IFS=(即从IFSfor 中删除所有空白字符,read否则会将它们从输入记录的末尾剥离)。-ras 否则read将反斜杠视为转义字符(用于字段和记录分隔符)。mv是终端(用于-i提示的答案),而不是来自find.