我有一个目录,其中包含一些子目录,其文件名和目录名包含一些点:
$ ls -R
.:
dir file.with.dots
./dir:
subdir.with.dots
./dir/subdir.with.dots:
other.file
我真的很难捕获所有这些文件,find
因为我不想重命名当前文件夹.
及其..
本身。
我还阅读了如何将文件名中的点替换为下划线,保持扩展名不变,但这并不适合这里,因为主要问题是文件的递归选择。
find
我尝试用和替换点tr
:
find $(pwd) -depth -name '*.*'|while read f; do mv -iv "$f" '"'$(echo $f|tr '.' '_')'"' ; done
但如果文件路径和文件本身都包含点,则会出现错误
如何重命名并用下划线替换所有出现的点?
答案1
和zsh
:
autoload zmv
zmv '(**/)(*.*)' '$1${2//./_}'
否则,如果您有权访问bash
(尽管ksh93
或zsh
也将这样做),您始终可以这样做:
find . -depth ! -name '.*' -name '*.*' -exec bash -c '
for file do
dir=${file%/*}
base=${file##*/}
mv -i -- "$file" "$dir/${base//./_}"
done' sh {} +
(尽管您会错过由 完成的健全性检查zmv
)。
这些(故意)不会重命名隐藏文件。
重点是按深度优先处理列表(zmv
默认情况下这样做,并使用-depth
in find
)并且仅重命名文件的基本名称。
这样你就可以:
mv -- a.b/c.d a.b/c_d
和然后:
mv -- a.b a_b
另请注意,对于非 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
(如果您的发现支持,您可以替换-exec printf '%s\0' {} +
为)。-print0
你需要:
-print0
/-exec printf '%s\0' {} +
因为您不能依赖换行符作为分隔符,因为换行符是文件名中的有效字符- 因此,您需要
-d ''
(read
读取直到 NUL 而不是 NL) IFS=
IFS
(即从for中删除所有空白字符read
,否则将它们从输入记录的末尾删除)。-r
否则read
将反斜杠视为转义字符(用于字段和记录分隔符)。- 那个与文件描述符跳舞的小家伙因为您希望 的标准输入
mv
成为终端(用于-i
提示的答案),而不是来自 的管道find
。