所有答案都参考旧版本的 bash_completion,与最近的 bash_completion 无关。
现代 bash_completion 默认将大部分完成文件移动到/usr/share/bash-completion/completions,通过运行检查系统上的路径
# pkg-config --variable=completionsdir bash-completion
/usr/share/bash-completion/completions
那里有很多文件,每个命令一个,但这不是问题,因为它们是在您第一次对每个命令使用完成时按需加载的。旧的/etc/bash_completion.d 仍然支持兼容性,并且在bash_completion 启动时会加载那里的所有文件。
# pkg-config --variable=compatdir bash-completion
/etc/bash_completion.d
使用此脚本检查旧目录中是否有任何陈旧文件。
#!/bin/sh
COMPLETIONS_DIR="$(pkg-config --variable=completionsdir bash-completion)"
COMPAT_DIR="$(pkg-config --variable=compatdir bash-completion)"
for file in "${COMPLETIONS_DIR}"/*; do
file="${COMPAT_DIR}/${file#${COMPLETIONS_DIR}/}"
[ -f "$file" ] && printf '%s\n' $file
done
它打印兼容目录中的文件列表,这些文件也存在于较新的(按需)完成目录中。除非您有特定理由保留其中一些文件,否则请查看、备份并删除所有这些文件。
因此,compat 目录应该大部分是空的。
现在,最有趣的部分是检查bash 启动缓慢的原因。
如果你只是运行bash,它将启动一个非登录的交互式 shell - 这个是 Cygwin 源代码/etc/bash.bashrc 然后~/.bashrc。这很可能不包括 bash 完成,除非您从 rc 文件之一获取它。如果您运行bash -l (bash --login),启动Cygwin Terminal(取决于您的cygwin.bat),或通过SSH 登录,它将启动一个登录、交互 shell - 这将提供/etc/profile、~/.bash_profile 和前面提到的 rc 文件。 /etc/profile 脚本本身在/etc/profile.d 中获取所有可执行的.sh 文件。
您可以检查每个文件的来源需要多长时间。在/etc/profile中找到此代码:
for file in /etc/profile.d/*.$1; do
[ -e "${file}" ] && . "${file}"
done
备份它,然后用这个替换它:
for file in /etc/profile.d/*.$1; do
TIMEFORMAT="%3lR ${file}"
[ -e "${file}" ] && time . "${file}"
done
启动bash,您将看到每个文件花费了多长时间。调查需要大量时间的文件。在我的例子中,它是bash_completion.sh 和fzf.sh(fzf 是模糊查找器,是对 bash_completion 的一个非常好的补充)。现在的选择是禁用它或进一步调查。由于我想继续在 bash 中使用 fzf 快捷方式,我调查了,找到了减速的根源,对其进行了优化,并将我的补丁提交给了 fzf 的 repo(希望它会被接受)。
现在是花费时间最多的人 - bash_completion.sh。基本上该脚本来源/usr/share/bash-completion/bash_completion。我备份了那个文件,然后编辑了它。在最后一页有for 循环,它获取compat 目录-/etc/bash_completion.d 中的所有文件。再次,我添加了TIMEFORMAT 和time,并查看了导致启动缓慢的脚本。它是zzz-fzf(fzf 包)。我调查并发现一个子shell ($()) 在for 循环中多次执行,在不使用子shell 的情况下重写了该部分,使脚本快速运行。我已经向 fzf 的仓库提交了我的补丁。
所有这些速度变慢的最大原因是:fork 不受 Windows 进程模型的支持,Cygwin 在模拟它方面做得很好,但与真正的 UNIX 相比,它的速度非常慢。一个 subshell 或一个单独做很少工作的管道将它的大部分执行时间花在fork-ing 上。例如。比较time echo msg(我的 Cygwin 上为 0.000 秒)与time echo $(echo msg)(我的 Cygwin 上为 0.042 秒)的执行时间——白天和黑夜。 echo 命令本身不会花费太多时间来执行,但创建子shell 的成本非常高。在我的 Linux 系统上,这些命令分别需要 0.000 秒和 0.001 秒。 Cygwin 的许多软件包都是由使用 Linux 或其他 UNIX 的人开发的,并且可以在未经修改的情况下在 Cygwin 上运行。因此,这些开发人员很自然地可以在方便的地方随意使用子外壳、管道和其他功能,因为他们不会觉得系统性能受到任何显着影响,但在 Cygwin 上,这些外壳脚本的运行速度可能会慢几十到几百倍。
底线,如果 shell 脚本在 Cygwin 中运行缓慢 - 尝试定位 fork 调用的来源并重写脚本以尽可能消除它们。
例如。 cmd="$(printf "$1" "$2")"(使用一个 fork 作为 subshell)可以替换为 printf -v cmd "$1" "$2"。
男孩,它出来的时间很长。读到这里的人都是真正的英雄。谢谢:)