天真的方法是find dir1 dir2 dir3 -type d -name .git | xargs -I {} dirname {}
,但对我来说太慢了,因为我在 git 存储库中有很多深层文件夹结构(至少我认为这是原因)。我读过有关我可以用来prune
防止 find 在发现某些内容后递归到目录中的内容,但有两件事。我不确定这是如何工作的(我的意思是,prune
尽管我读过手册页,但我不明白它的作用),第二个它在我的情况下不起作用,因为它会阻止find
递归到.git
文件夹中,但不会递归到所有文件夹中其他文件夹。
所以我真正需要的是:
对于所有子目录,检查它们是否包含.git
文件夹,如果是,则停止在此文件系统分支中搜索并报告结果。如果这也能从搜索中排除任何隐藏目录,那就完美了。
答案1
好吧,我仍然不完全确定它是如何工作的,但我已经测试过它并且它有效。
.
├── a
│ ├── .git
│ └── a
│ └── .git
└── b
└── .git
6 directories, 0 files
% find . -type d -exec test -e '{}/.git' ';' -print -prune
./a
./b
我期待着更快地做到这一点。
答案2
理想情况下,您希望爬行目录树以查找包含条目的目录.git
,并停止进一步搜索这些目录(假设您在 git 存储库中没有更多的 git 存储库)。
问题是,使用 standard 时find
,执行这种检查(目录包含条目.git
)涉及生成一个使用谓词执行test
实用程序的进程-exec
,这比列出几个目录的内容效率要低。
一个例外是如果您使用shellfind
的内置函数bosh
(由 Bourne shell 开发的 POSIXified 分支)@希利)它有一个-call
谓词来评估 shell 中的代码,而无需生成新的 sh 解释器:
#! /path/to/bosh -
find . -name '.?*' -prune -o \
-type d -call '[ -e "$1/.git" ]' {} \; -prune -print
或者perl
使用File::Find
:
perl -MFile::Find -le '
sub wanted {
if (/^\../) {$File::Find::prune = 1; return}
if (-d && -e "$_/.git") {
print $File::Find::name; $File::Find::prune = 1
}
}; find \&wanted, @ARGV' .
zsh
比's printf '%s\n' **/.git(:h)
(下降到所有非隐藏目录)或 GNU find
's (在每个非隐藏目录的新进程中find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -print
运行一个命令)更长,但更快。test
2022年编辑。最新版本的 busybox 中的小find
程序能够运行其[
或test
小程序,而无需分叉进程并在内部重新执行自身,因此,尽管它仍然不如 bosh 或 perl 方法那么快:
busybox find . -type d -exec [ -e '{}/.git' ] ';' -prune -print
在我的测试中,比 GNU 等效项快几个数量级find
(在包含 git / cvs / svn 存储库混合的本地样本上,总共超过 100000 个目录,我得到的 bosh 为 0.25 秒,perl 为 0.3 秒,busybox 为 0.7 秒)find
,GNU 为 36 秒find
,GNU 为 2 秒find . -name .git -printf '%h\n'
(给出不同的结果,因为它还.git
在 git 存储库的子目录中查找文件)。
答案3
如果您使用locate,您可以找到具有以下内容的目录:
locate .git | grep "/.git$"
结果列表很快,进一步处理也很容易。
答案4
您也可以使用ls -d */.git
然后.git
在最后剥去。
这不会查找子文件夹。