我需要重命名目录中的多个目录,以在名称开头包含下划线“_”字符。
作为测试,我创建了一个父目录,其内容包含三个目录:
dir1, dir2 and dir3; three files: file1, file2, file3
和三个点前缀文件:
.file1, .file2, .file3
到目前为止,我已经尝试在 Mac 上gfind
安装后使用 , :findutils
gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
这将返回:
mv: rename ./. to ./_.: Invalid argument
所以,我尝试使用重命名和查找:
find . -type d -exec rename 's/^/_/' {} \;
这将返回:
Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory
有人碰巧有一个可行的解决方案或者知道我可能会在哪里犯错吗?
答案1
如果我没猜错的话,你会有这样的文件和目录:
$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1 .file2 .file3 dir1 dir2 dir3 file1 file2 file3
并想将目录重命名为_dir1
, _dir2
, _dir3
?
那find
/printf mv
看起来可行。让我们看看它打印了什么:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"
第一个命令会抛出错误,因为“点”很特殊,您无法重命名它。但其他的应该没问题,并且应该根据需要重命名目录:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1 _dir2 _dir3
但是通过管道将命令传送到 shell 有点难看,如果文件名足够奇怪,结果将会令人惊讶(例如,如果它们包含$
将在 shell 中触发变量或命令替换的符号)。
如果文件都在一个级别上,那么应该这样做:
for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done
(尽管如果_$x
已经存在,$x
则会被移入其中。)
如果要包含名称以点开头的目录,请shopt -s dotglob
提前使用。
这也很接近:
find . -type d -exec rename 's/^/_/' {} \;
但由于find
给出了rename
以 开头的路径./
,我们需要考虑到这一点。仅在一个层面上,这应该可以(将前导更改./
为./_
):
find . -type d -exec rename 's,^./,./_,' {} \;
要获取所有级别的目录,find -execdir
可能是最容易使用的。它在文件的目录中运行命令。我们需要-depth
以正确的顺序处理重命名。
find . -depth -type d -execdir rename 's,^./,./_,' {} \;
或许也可以添加! -name .
。例如
$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls _foo
_dir1 _dir2
答案2
您第一次尝试的问题是,.
除了所有子目录之外,您还尝试重命名自身(当前目录)。跳过它。
gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
但实际上,不要这样做。切勿将东西通过管道输入sh
.这不起作用除非您的文件名碰巧不包含任何 shell 特殊字符。如果您有一个名为`rm -r ~`
(完全有效,但不常见的文件名)的文件,则 pop 会进入您的主目录。调用 shell,-exec
如下所示。
您的第二次尝试不起作用,因为您_
在完整路径的开头插入 ,而不是在基本名称的开头添加它,即在最后一个/
.
find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;
如果您有嵌套目录,这些实际上都不起作用,因为它们在find
遍历目录时会重命名目录。当您执行此操作时,您需要先对目录的内容进行操作,然后再使用该-depth
选项对目录本身进行操作。
find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;
更方便的是,调用 shell 并运行mv
。这适用于任何 POSIX 系统。
find . -depth -type d -name . -o -exec sh -c '
for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +
如果没有嵌套子目录,那么您可以使用简单的 shell 循环。如果不想重命名以点开头的目录,只需使用通配符*/
,它只匹配目录和目录的符号链接。
for d in */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi # robustness in case there are no subdirectories
if [ -L "$d" ]; then continue; fi # optional: skip symbolic links to directories
mv -- "$d" "_${d%/}"
done
如果要重命名所有目录(包括名称以点开头的目录),请.*
同时包含通配符。
for d in .*/ */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
if [ -L "$d" ]; then continue; fi
mv -- "$d" "_${d%/}"
done
请注意,当您移动foo
到时_foo
,如果已经有一个名为 的目录_foo
,则会将旧目录移动foo
到_foo/foo
。为了防止这种情况,请添加显式测试。
或者,也可以使用 zsh。它预装在 macOS 上。第一次运行autoload -U zmv
(将其放入您的.zshrc
或您的脚本中)以加载zmv
功能,那么非递归版本(包括点文件,不包括符号链接 - (/D)
是一组全局限定符):
zmv -Q '*(/D)' '_$f'
或递归版本(使用修饰语提取目录名和基本名):
zmv -Q '**/*(/D)' '$f:h/_$f:t'
zmv
如果目标已经存在,将拒绝采取行动。