jav*_*vex 5 zsh zsh-completion
我正在使用 zsh 并打开完成功能。当我尝试制表符完成时,有时命令会挂起很长时间。几秒钟后,它完成并正确呈现我的选项。另一方面,如果我用 Ctrl-C 中断它,我会收到以下消息:
Killed by signal in _path_commands after 2s
Run Code Online (Sandbox Code Playgroud)
如果我尝试使用制表符完成目录(例如在 中ls),它工作得很好,没有延迟。
请注意,我正在使用 WSL2 在 Windows 上运行,尽管我依稀记得它也发生在其他系统上。还没有回去确认,但是当我刚刚在我的服务器上测试时,我无法在那里重现它,所以这与环境有关。
提供我自己的问题的答案以分享我的发现。如果其他人有更好的想法,我很乐意接受他们的答案。然而,当谷歌搜索时,我找不到任何关于这个错误的信息(zsh\ 晦涩的语法没有帮助,使它像 perl 表达式一样容易谷歌搜索)。
\ntl ;dr解决方案如下:运行unsetopt pathdirs,问题就会消失。将其放入您的中~/.zshrc,应该可以解决。接下来是解释。
打开跟踪以_path_commands查看它挂在哪里autoload -t _path_commands::
+_path_commands:46> ret=0\n+_path_commands:51> [[ -o path_dirs ]]\n+_path_commands:52> local -a path_dirs\nRun Code Online (Sandbox Code Playgroud)\n因此,让我们通过以下方式查看该函数which _path_commands(请注意,您需要执行一次补全操作,以便 zsh 加载它)。我将提供相关片段:
if [[ -o path_dirs ]]\n then\n local -a path_dirs\n path_dirs=(${^path}/*(/N:t))\n (( ${#path_dirs} )) && _wanted path-dirs expl \'directory in path\' compadd "$@" -a path_dirs && ret=0\n if [[ $PREFIX$SUFFIX = */* ]]\n then\n _wanted commands expl \'external command\' _path_files -W path -g \'*(*)\' && ret=0\n fi\n fi\nRun Code Online (Sandbox Code Playgroud)\n当它挂起时我们得到的最后一行local -a path_dirs只是定义了一个空数组。可能不是这样,但是如果我执行下一个命令,它会挂起很长时间:path_dirs=(${^path}/*(/N:t))。如果您不熟悉该语言,祝您好运。我会解释一下:
( ... )$pathRC_EXPAND_PARAM 我们用^字符来开启${^path},参见14.3参数扩展。这不是我们的罪魁祸首,所以我将跳过解释。唯一需要理解的是我们这里有一个数组。/*。这与您在命令行上执行此操作相同:ls *例如。但这里除外,它对数组的所有元素执行此操作,就像循环一样。一个很好的罪魁祸首,但如果我们尝试的echo ${^path}/*话,速度仍然很快。/只返回目录N sets nullglob, basically "remove empty elements":t sets the modifier to remove the full path and leave only the basename output.If we play around with the full expression e.g. ${^path}/*(/N:t) we notice that it\'s only slow if the / character is present. Removing it makes everything fast. With some additional debugging you can even find what\'s slow, e.g. write a loop and see when it hangs:
for item in $path; do echo "${item}: " ${item}/*(/); done\nRun Code Online (Sandbox Code Playgroud)\nIn my case I notice it hanging on a lot of Windows paths (/mnt/c/Windows/system32, for example). At this point I gave up: I don\'t know why this expansion is so slow for Windows paths and I don\'t know how to debug it or do some form of "caching" that speeds it up (it might just be slow due to WSL filesystem issues).
Instead, notice how there is a condition: if [[ -o path_dirs ]] before entering this code path? The conditional test -o checks for an option, i.e. if path_dirs is set. This is described in the options manual:
\n\nPATH_DIRS (-Q)
\n
\n\nPerform a path search even on command names with slashes in them. Thus if \xe2\x80\x98/usr/local/bin\xe2\x80\x99 is in the user\xe2\x80\x99s path, and he or she types \xe2\x80\x98X11/xinit\xe2\x80\x99, the command \xe2\x80\x98/usr/local/bin/X11/xinit\xe2\x80\x99 will be executed (assuming it exists). Commands explicitly beginning with \xe2\x80\x98/\xe2\x80\x99, \xe2\x80\x98./\xe2\x80\x99 or \xe2\x80\x98../\xe2\x80\x99 are not subject to the path search. This also applies to the \xe2\x80\x98.\xe2\x80\x99 and source builtins.
\n
If we can live without this feature (I think I can), we can stop here: Simply turn it off, e.g. via unsetopt pathdirs and call it a day. Once that\'s done, this code branch is no longer executed and the problem goes away.