19

目前 bash 需要大约 2 秒才能加载。我已经用-xflag 运行了 bash,我看到了输出,似乎 PATH 在 cygwin 中被加载了很多次。有趣的是我在 linux 环境中使用相同的文件,但它工作正常,没有重新加载问题。以下是否会导致问题?

if [ `uname -o` = "Cygwin" ]; then
    ....
fi
4

9 回答 9

21

正如您在回答中指出的那样,问题在于 Cygwin 的 bash-completion 包。快速简单的解决方法是禁用 bash-completion,正确的方法是运行 Cygwin 的 setup.exe(如果需要,请再次下载)并选择卸载该软件包。

更长的解决方案是处理文件/etc/bash_completion.d并禁用您不需要的文件。在我的系统上,减慢 Bash 加载时间的最大罪魁祸首(mailman、shadow、dsniff 和 e2fsprogs)都没有做任何事情,因为没有安装它们被创建来完成的工具。

如果您将文件重命名/etc/bash_completion.d为具有.bak扩展名,它将停止加载该脚本。以这种方式在我的一个系统上禁用了除选定的 37 个脚本之外的所有脚本后,我将 bash_completion 加载的平均时间缩短了 95%(6.5 秒到 0.3 秒)。

于 2013-03-26T20:57:41.503 回答
15

就我而言,那是 Windows 域控制器。我这样做是为了找到问题:

我从一个简单的 windows 开始,cmd.exe然后输入:
c:\cygwin\bin\strace.exe c:\cygwin\bin\bash

就我而言,我注意到以下顺序:

    218   12134 [main] bash 11304 transport_layer_pipes::connect: Try to connect to named pipe: \\.\pipe\cygwin-c5e39b7a9d22bafb-lpc
     45   12179 [main] bash 11304 transport_layer_pipes::connect: Error opening the pipe (2)
     39   12218 [main] bash 11304 client_request::make_request: cygserver un-available
1404719 1416937 [main] bash 11304 pwdgrp::fetch_account_from_windows: line: <CENSORED_GROUP_ID_#1>
    495 1417432 [main] bash 11304 pwdgrp::fetch_account_from_windows: line: <CENSORED_GROUP_ID_#2>
    380 1417812 [main] bash 11304 pwdgrp::fetch_account_from_windows: line: <CENSORED_GROUP_ID_#3>

    etc...

关键是确定client_request::make_request: cygserver un-available线路。您可以看到,在那之后,cygwin 如何尝试从 Windows 中获取每个组,并且执行时间变得疯狂。

一个快速的谷歌揭示了 acygserver是什么: https ://cygwin.com/cygwin-ug-net/using-cygserver.html

Cygserver 是一个旨在作为后台服务运行的程序。它为 Cygwin 应用程序提供需要安全仲裁或需要在没有其他 cygwin 应用程序运行时持续存在的服务。

解决方案是,运行cygserver-config然后net start cygserver启动 Windows 服务。Cygwin 的启动时间在那之后显着下降。

于 2015-02-18T14:32:59.410 回答
6

所有答案都指的是旧版本的 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脚本.sh本身在/etc/profile.d.

您可以检查每个文件需要多长时间才能获取。在以下位置找到此代码/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.shand fzf.sh(fzf 是模糊查找器,对 bash_completion 的一个非常好的补充)。现在的选择是禁用它或进一步调查。由于我想继续fzf在 bash 中使用快捷方式,因此我进行了调查,找到了减速的根源,对其进行了优化,并将我的补丁提交给了 fzf 的 repo(希望它会被接受)。

现在是最大的时间花费者 - bash_completion.sh。基本上就是脚本来源/usr/share/bash-completion/bash_completion。我备份了那个文件,然后编辑了它。在最后一页有一个for循环,它获取compatdir -中的所有文件/etc/bash_completion.d。再次,我添加了TIMEFORMATand time,并查看了导致启动缓慢的脚本。它是zzz-fzffzf包)。我调查并发现一个子shell($())在循环中多次执行for,重写了该部分而不使用子shell,使脚本快速运行。我已经将我的补丁提交给了 fzf 的仓库。

所有这些减速的最大原因是:fork不受 Windows 进程模型的支持,Cygwin 在模拟它方面做得很好,但与真正的 UNIX 相比,它的速度慢得令人痛苦。一个 subshel​​l 或一个只做很少工作的管道将大部分的执行时间花在fork-ing 上。例如,比较(我的 Cygwin 上的time echo msg0.000 秒)与time echo $(echo msg)(我的 Cygwin 上的 0.042 秒)的执行时间——白天和黑夜。这echo命令本身不会花费太多时间来执行,但是创建一个子shell 是非常昂贵的。在我的 Linux 系统上,这些命令分别需要 0.000 秒和 0.001 秒。Cygwin 的许多软件包都是由使用 Linux 或其他 UNIX 的人开发的,并且可以在未经修改的情况下在 Cygwin 上运行。因此,这些开发人员自然而然地可以在方便的地方随意使用子shell、管道和其他功能,因为他们不会感觉到对他们的系统有任何显着的性能影响,但是在 Cygwin 上,这些 shell 脚本的运行速度可能会慢几十到几百倍。

底线,如果 shell 脚本在 Cygwin 中运行缓慢 - 尝试定位fork调用源并重写脚本以尽可能消除它们。例如cmd="$(printf "$1" "$2")"(使用一个 fork 作为 subshel​​l)可以替换为printf -v cmd "$1" "$2".

男孩,它出来的时间很长。读到这里的人都是真正的英雄。谢谢 :)

于 2016-04-23T20:10:00.360 回答
4

我知道这是一个旧线程,但是在本周全新安装 Cygwin 后,我仍然遇到这个问题。

我没有手工挑选所有的 bash_completion 文件,而是使用这一行来实现 @me_and 的方法来处理我机器上没有安装的任何东西。这对我来说显着减少了 bash 的启动时间。

/etc/bash_completion.d中,执行以下操作:

for i in $(ls|grep -v /); do type $i >/dev/null 2>&1 || mv $i $i.bak; done
于 2014-10-24T20:11:49.663 回答
3

PATH与原始问题有关的旧线程的新答案。

大多数其他答案都与 bash 启动有关。如果您在bash -ishell 中运行时看到加载时间很慢,那么这些可能适用。

在我的情况下,bash -i运行速度很快,但每当我打开一个新的 shell(无论是在终端中还是在 xterm 中),都需要很长时间。如果bash -l是需要很长时间,这意味着它是登录时间。

https://cygwin.com/faq/faq.html#faq.using.startup-slow的 Cygwin 常见问题解答中有一些方法,但它们对我不起作用。

最初的海报询问了PATH他诊断使用的bash -x. 我也发现虽然bash -i速度很快,但速度bash -xl很慢,并且显示了很多关于PATH.

有一个长得可笑的Windows PATH,登录过程一直在运行程序并在整个程序中搜索PATH正确的程序。

我的解决方案:编辑 WindowsPATH以删除任何多余的东西。我不确定我删除的哪个部分起到了作用,但登录 shell 启动时间从 6 秒缩短到了 1 秒以下。

YMMV。

于 2015-03-11T20:11:51.033 回答
2

我的回答和上面的npe一样。但是,由于我刚刚加入,我无法评论甚至投票!我希望这篇文章不会被删除,因为它为任何寻找相同问题答案的人提供了保证。

npe 的解决方案对我有用。只有一个警告 - 在我充分利用它之前,我必须关闭所有 cygwin 进程。这包括运行 cygwin 服务,如 sshd,以及我从登录脚本启动的 ssh-agent。在此之前,cygwin 终端的窗口会立即出现,但会在出现提示之前挂起几秒钟。关闭窗口后它挂了几秒钟。在我杀死所有进程并启动 cygserver 服务之后(顺便说一句,我更喜欢使用 Cygwin 方式 - 'cygrunsrv -S cygserver',而不是 'net start cygserver';我不知道它是否有任何实际区别)它立即启动。所以再次感谢npe!

于 2015-02-20T12:00:48.113 回答
2

我在一个设置非常复杂的公司网络上,这似乎真的扼杀了 cygwin 的启动时间。与 npe 的回答相关,我还必须遵循此处列出的一些步骤:https ://cygwin.com/faq/faq.html#faq.using.startup-slow

AD 客户端系统的另一个原因是 DC 响应缓慢,通常在具有远程 DC 访问的配置中观察到。Cygwin DLL 查询有关您所在的每个组的信息,以便在启动时填充本地缓存。您可以通过将自己的信息缓存在本地文件中来稍微加快此过程。在对 /etc 具有写访问权限的 Cygwin 终端中运行这些命令:

getent passwd $(id -u) > /etc/passwd
getent group $(id -G) > /etc/group

另外,设置/etc/nsswitch.conf如下:

passwd: files db
group: files db

这将限制 Cygwin 联系 AD 域控制器 (DC) 的需要,同时仍允许从 DC 检索其他信息,例如在列出远程目录时。

在这样做加上启动 cygserver 之后,我的 cygwin 启动时间显着下降。

于 2015-06-01T15:02:13.963 回答
0

正如上面有人提到的,一个可能的问题是 PATH 环境变量包含太多路径,cygwin 将搜索所有这些。我更喜欢直接编辑 /etc/profile,只需将 PATH 变量覆盖到 cygwin 相关路径,例如PATH="/usr/local/bin:/usr/bin". 如果需要,请添加其他路径。

于 2015-10-19T03:27:21.987 回答
0

我编写了一个名为“minimizecompletion”的 Bash 函数,用于停用不需要的完成脚本。

完成脚本可以添加多个完成规范或具有用于 shell 构建的完成规范,因此将脚本名称与 $PATH 中找到的可执行文件进行比较是不够的。

我的解决方案是删除所有加载的完成规范,加载完成脚本并检查它是否添加了新的完成规范。根据这一点,通过将 .bak 添加到脚本文件名来禁用它,或者通过删除 .bak 来激活它。对 /etc/bash_completion.d 中的所有 182 个脚本执行此操作会导致 36 个活动和 146 个非活动完成脚本将 Bash 启动时间减少 50%(但应该清楚这取决于已安装的包)。

该函数还检查未激活的完成脚本,以便在新安装的 Cygwin 软件包需要它们时激活它们。可以使用激活所有脚本的参数 -a 撤消所有更改。

# Enable or disable global completion scripts for speeding up Bash start.
#
# Script files in directory '/etc/bash_completion.d' are inactived
# by adding the suffix '.bak' to the file name; they are activated by
# removing the suffix '.bak'. After processing all completion scripts
# are reloaded by calling '/etc/bash_completion'
#
# usage:  [-a]
#         -a  activate all completion scripts
# output: statistic about total number of completion scripts, number of
#         activated, and number of inactivated completion scripts; the
#         statistic for active and inactive completion scripts can be
#         wrong when 'mv' errors occure
# return: 0   all scripts are checked and completion loading was
#             successful; this does not mean that every call of 'mv'
#             for adding or removing the suffix was successful
#         66  the completion directory or loading script is missing
#
minimizecompletion() {
  local arg_activate_all=${1-}
  local completion_load=/etc/bash_completion
  local completion_dir=/etc/bash_completion.d

  (
    # Needed for executing completion scripts.
    #
    local UNAME='Cygwin'
    local USERLAND='Cygwin'
    shopt -s extglob progcomp
    have() {
      unset -v have
      local PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin"
      type -- "$1" &>/dev/null && have='yes'
    }

    # Print initial statistic.
    #
    printf 'Completion scripts status:\n'
    printf '  total:       0\n'
    printf '  active:      0\n'
    printf '  inactive:    0\n'
    printf 'Completion scripts changed:\n'
    printf '  activated:   0\n'
    printf '  inactivated: 0\n'

    # Test the effect of execution for every completion script by
    # checking the number of completion specifications after execution.
    # The completion scripts are renamed depending on the result to
    # activate or inactivate them.
    #
    local completions total=0 active=0 inactive=0 activated=0 inactivated=0
    while IFS= read -r -d '' f; do
      ((++total))
      if [[ $arg_activate_all == -a ]]; then
        [[ $f == *.bak ]] && mv -- "$f" "${f%.bak}" && ((++activated))
        ((++active))
      else
        complete -r
        source -- "$f"
        completions=$(complete | wc -l)
        if (( $completions > 0 )); then
          [[ $f == *.bak ]] && mv -- "$f" "${f%.bak}" && ((++activated))
          ((++active))
        else
          [[ $f != *.bak ]] && mv -- "$f" "$f.bak" && ((++inactivated))
          ((++inactive))
        fi
      fi
      # Update statistic.
      #
      printf '\r\e[6A\e[15C%s' "$total"
      printf '\r\e[1B\e[15C%s' "$active"
      printf '\r\e[1B\e[15C%s' "$inactive"
      printf '\r\e[2B\e[15C%s' "$activated"
      printf '\r\e[1B\e[15C%s' "$inactivated"
      printf '\r\e[1B'
    done < <(find "$completion_dir" -maxdepth 1 -type f -print0)

    if [[ $arg_activate_all != -a ]]; then
      printf '\nYou can activate all scripts with %s.\n' "'$FUNCNAME -a'"
    fi
    if ! [[ -f $completion_load && -r $completion_load ]]; then
      printf 'Cannot reload completions, missing %s.\n' \
             "'$completion_load'" >&2
      return 66
    fi
  )

  complete -r
  source -- "$completion_load"
}

这是一个示例输出和结果时间:

$ minimizecompletion -a
Completion scripts status:
  total:       182
  active:      182
  inactive:    0
Completion scripts changed:
  activated:   146
  inactivated: 0

$ time bash -lic exit
logout

real    0m0.798s
user    0m0.263s
sys     0m0.341s

$ time minimizecompletion
Completion scripts status:
  total:       182
  active:      36
  inactive:    146
Completion scripts changed:
  activated:   0
  inactivated: 146

You can activate all scripts with 'minimizecompletion -a'.

real    0m17.101s
user    0m1.841s
sys     0m6.260s

$ time bash -lic exit
logout

real    0m0.422s
user    0m0.092s
sys     0m0.154s
于 2016-01-22T21:30:32.507 回答