我已经花时间将脚本复制粘贴到您的问题中,并将其与您自己的答案的脚本进行比较。这里有一些有趣的结果:
请注意:
- 我
git pull
通过在它们前面加上前缀来禁用echo
- 我也删除了颜色的东西
- 我还删除
.ignore
了解决方案中的文件测试bash
。
> /dev/null
并在这里和那里删除了不必要的东西。
- 删除
pwd
了两者中的调用。
- 添加了示例
-prune
中显然缺少的find
find
使用“while”而不是“for”,这在示例中也适得其反
- 相当多地解开了第二个例子以达到重点。
- 添加了对
bash
解决方案的测试以不遵循符号链接以避免循环并充当查找解决方案。
- 添加
shopt
以允许*
扩展为虚线目录名称,以匹配find
解决方案的功能。
因此,我们正在比较基于查找的解决方案:
#!/bin/bash
find . -name .git -type d -prune | while read d; do
cd $d/..
echo "$PWD >" git pull
cd $OLDPWD
done
使用bash shell 构建解决方案:
#!/bin/bash
shopt -s dotglob
update() {
for d in "$@"; do
test -d "$d" -a \! -L "$d" || continue
cd "$d"
if [ -d ".git" ]; then
echo "$PWD >" git pull
else
update *
fi
cd ..
done
}
update *
注意:内置函数 (function
和for
) 不受 MAX_ARGS OS 启动进程的限制。所以*
不会在非常大的目录上收支平衡。
解决方案之间的技术差异:
基于查找的解决方案使用 C 函数来爬取存储库,它:
- 必须为该
find
命令加载一个新进程。
- 将避免“.git”内容,但会抓取 git 存储库的工作目录,并在其中丢失一些时间(并最终找到更多匹配的元素)。
- 每次匹配都必须
chdir
通过几个深度的子目录并返回。
- 必须
chdir
在 find 命令中执行一次,在 bash 部分中执行一次。
基于 bash 的解决方案使用内置(因此接近 C 实现,但已解释)来爬取存储库,请注意:
- 将只使用一个进程。
- 将避免 git workdir 子目录。
- 一次只会执行
chdir
一个级别。
- 只会执行
chdir
一次查找和执行命令。
解决方案之间的实际速度结果:
我有一个 git 存储库的工作开发集合,我在其上启动了脚本:
- 找到解决方案:~0.080s(bash chdir 需要 ~0.010s)
- bash 解决方案:~0.017s
我不得不承认,我没有准备好从 bash 内置函数中看到这样的胜利。在对发生的事情进行分析后,它变得更加明显和正常。雪上加霜,如果您将外壳从/bin/bash
to更改/bin/sh
(您必须注释掉该shopt
行,并准备好它不会解析虚线目录),您将跌至 ~0.008s 。打败那个 !
请注意,您可以使用以下方法更聪明地使用 find 解决方案:
find . -type d \( -exec /usr/bin/test -d "{}/.git" -a "{}" != "." \; -print -prune \
-o -name .git -prune \)
这将有效地删除在找到的 git 存储库中对所有子存储库的爬取,其代价是为每个爬取的目录生成一个进程。我得到的最终 find 解决方案大约是 ~0.030s,比之前的 find 版本快两倍多,但仍然比 bash 解决方案慢 2 倍。
请注意,/usr/bin/test
避免搜索$PATH
花费时间很重要,而且我需要-o -name .git -prune
,-a "{}" != "."
因为我的主存储库本身就是一个 git 子存储库。
作为结论,我不会使用 bash 内置解决方案,因为它对我来说有太多的极端情况(而且我的第一次测试遇到了一个限制)。但对我来说重要的是要解释为什么在某些情况下它可以(快得多),但find
解决方案对我来说似乎更加健壮和一致。