TL;DR (或者应该是 TL;WR - “太长,不会读”?)
alias recursively_git_pull_all_repo='for dir in **/.git(/:h); git -C $dir pull'
您至少部分正确,因为该命令的一部分在声明别名时运行。这有两个效果:
- 加载别名需要时间
- 别名不起作用
说明
您在双引号中声明别名,这允许参数扩展。这意味着 $(find . -name ".git") 和 ${dir%/*} 部分将在声明别名时扩展并替换为它们当时产生的值。
对于$(find . -name ".git"),这意味着. 很可能是$HOME,并且该构造被替换为您的主目录中所有.git 目录(和其他类似文件的对象)的换行符分隔列表。
${dir%/*} 可能会被空字符串替换,因为dir 很可能未设置。记住:那个时候别名本身并没有被执行,所以dir不会被for循环设置。
这意味着:
alias recursively_git_pull_all_repo="for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done"
将有效地保存为类似的东西
alias recursively_git_pull_all_repo="for dir in docs/repo1/.git
docs/repo2/.git
otherdocs/repo/.git
whatever/.git; do
cd ; git pull ; cd -; done"
幸运的是,这将失败并出现zsh: command not found: docs/repo2/.git 错误,因为第一个存储库之后的换行符。如果没有,它会反复切换到你的主目录(cd不带参数)并尝试做git pull。
解决方案
快速的解决方案是在声明别名时只使用单引号,这样命令和参数将在运行别名时替换,而不是在声明时替换。
但是还有其他一些问题:
- 如果任何目录名称包含空格,这将失败,因为 for 会将其解释为两个值
- 如果您无法切换到找到的目录之一,
git pull 将从当前目录运行。如果路径中的目录没有设置 execute 标志,这实际上可能发生。
- 没有附加参数
find 也会列出名为.git 的非目录
改为我建议使用zsh 提供的功能:
alias recursively_git_pull_all_repo='for dir in **/.git(/:h); git -C $dir pull'
- 您实际上并不需要
find 来获取目录列表。 ** glob 将递归地扩展到所有目录。所以**/.git 将扩展到所有以.git 作为最后一个元素的路径。
- 您可以使用 glob 限定符将扩展限制为满足特定条件的元素。在这种情况下,
**/.git(/) 限定符 / 表示:“仅目录”。
- 您还可以将历史扩展修饰符用作全局限定符。这是通过
: 后跟修饰符完成的 - 在本例中为 h,它删除了最后一个路径组件(如您示例中的程序 dirname 或 ${dir%/*})。
- 您实际上不需要将目录更改为 git 存储库即可使用
git;你可以告诉git 在哪里使用git -C <path>
- 由于您现在只在循环内运行一个命令,因此您可以使用
for 的简短语法,即不使用do ...; done。
有趣的事实:如果您使用d 而不是dir,另一种简短形式的for 并且启用了选项GLOB_STAR_SHORT(需要zsh >= 5.2),您可以使用:
for d (**.git(/:h))git -C $d pull
仅比您的别名长 4 个字符。是的,我知道完成。但也有历史搜索?。