【问题标题】:zshrc alias gets executed when launching a new terminalzshrc 别名在启动新终端时被执行
【发布时间】:2016-03-09 15:04:41
【问题描述】:

我正在使用zsh,并在我的配置中添加另一个别名:

alias recursively_git_pull_all_repo="for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done"

但是,每次我打开一个新终端时,这个别名似乎都会被执行(或者至少它会大大减慢启动一个新终端的速度)。

如何在每次打开新终端时添加此别名而不启动它?

【问题讨论】:

    标签: zshrc oh-my-zsh


    【解决方案1】:

    TL;DR (或者应该是 TL;WR - “太长,不会读”?)

    alias recursively_git_pull_all_repo='for dir in **/.git(/:h); git -C $dir pull'
    

    您至少部分正确,因为该命令的一部分在声明别名时运行。这有两个效果:

    1. 加载别名需要时间
    2. 别名不起作用

    说明

    您在双引号中声明别名,这允许参数扩展。这意味着 $(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 个字符。是的,我知道完成。但也有历史搜索?。

    【讨论】:

    • 非常好的解释。我将不得不重新审视我现有的一些有效别名,但这可能是纯粹的运气。在.bashrc 中,您会使用@Hashier 建议的函数方法吗?
    • 也许吧?即使在zsh 的情况下,它也有点折腾:别名或函数,无论哪种方式都可以。我主要使用别名,因为它在问题中并且没有真正需要函数。 bash 没有 glob 限定符,因此需要使用 find,并且在引用时需要格外小心(例如,您必须在 $dir 周围使用双引号来避免空格)。所以我可能在那里使用了一个函数,主要是为了保持它的可读性。
    • 非常好的答案。谢谢你的解释。在我的情况下,我只需要将别名中的所有" 替换为'(当然不是在实际命令中)。 ZSH 启动时间从 20 秒缩短到 2 秒! (是的,我有很多别名)
    【解决方案2】:

    你可以把它放在一个函数中而不是一个别名中:

    $ cat zshFunction
    
    function recursively_git_pull_all_repo () {
        echo "Executing code"
        for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done
    }
    
    $ recursively_git_pull_all_repo
    zsh: command not found: recursively_git_pull_all_repo
    
    $ source zshFunction                                                                                                                                                      
    $
    
    $ recursively_git_pull_all_repo
    Executing code
    $
    

    【讨论】:

    • 仍然不建议循环遍历这样的目录。但是对于别名的函数 +1。
    猜你喜欢
    • 2018-07-25
    • 2019-10-21
    • 1970-01-01
    • 2014-09-21
    • 1970-01-01
    • 2018-04-17
    • 2014-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多