从 bash_profile 在后台获取长时间运行的脚本

从 bash_profile 在后台获取长时间运行的脚本

.bash_profile每次打开终端时,我的脚本都需要几秒钟才能运行,因此这变得非常烦人。

我的测试表明某些命令需要很长时间,因此我将它们移出.bash_profile到新脚本中.bash_profile_load_in_background

.bash_profile正在尝试在后台寻找来源。

.bash_profile

# fast stuff here

#.....

# slow stuff here

source .bash_profile_load_in_background & # notice the &

我正在设置一些变量,但当发送到后台.bash_profile_load_in_background时它们没有正确传播。source&

这是我的“慢”脚本的删节版本:

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm

[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

if type brew &>/dev/null; then
  HOMEBREW_PREFIX="$(brew --prefix)"
  if [[ -r "${HOMEBREW_PREFIX}/etc/profile.d/bash_completion.sh" ]]; then
    source "${HOMEBREW_PREFIX}/etc/profile.d/bash_completion.sh"
  else
    for COMPLETION in "${HOMEBREW_PREFIX}/etc/bash_completion.d/"*; do
      [[ -r "$COMPLETION" ]] && source "$COMPLETION"
    done
  fi
fi

if [ -f $(brew --prefix)/etc/bash_completion ]; then
 . $(brew --prefix)/etc/bash_completion
fi

# parse the git branch
gb() {
     __git_ps1
}

# change the prompt to show working directory and git branch
PS1="\[\e[32m\]\w \[\e[91m\]\$(gb)\[\e[00m\]? "

很容易看出,PS1一旦后台脚本完成,提示就不会改变。

另一方面,如果我以“阻止”内联方式获取脚本,一切都会按预期工作:

IE

调整

source .bash_profile_load_in_background & # notice the &

source .bash_profile_load_in_background # REMOVE THE &

为什么会发生这种情况?

.bash_profile有什么办法可以以简单的方式完成这项工作并加快加载时间?我不在乎有些功能是否需要几秒钟才能生效,但它必须在当前范围内生效。

答案1

诡计

这个另一个答案解释为什么你的尝试不起作用。有一个技巧可以让它发挥作用,至少在某种程度上。我真的不推荐它,这不是.bash_profile应该如何工作。

想法是这样的:

  1. 让我们.bash_profile创建一个临时文件并记住它的路径:

    tmpf="$(mktemp)"
    

    mktemp不可携带。)

  2. 设置 a trap,以便 shell 将临时文件作为源SIGUSR1。获取后,不再需要文件和陷阱,因此请考虑将清理作为陷阱的一部分。

    trap '
      . "$tmpf"
      rm "$tmpf"
      trap - USR1
    ' USR1
    
  3. .bash_profile_background_wizard在后台运行。该脚本必须能够写入临时文件,因此给它一个描述符或路径(作为参数或在环境中)。例子:

    tmpf="$tmpf" ~/.bash_profile_background_wizard &
    

    该脚本应启动长时间运行的任务并将 shell 代码写入临时文件。它应该建造函数和变量的定义、依赖于长时间运行的任务的任何内容、您希望主 shell 在准备就绪时“采用”的任何内容。根据您想要做什么,编写脚本的脚本可能并不容易。记住临时文件即将解析的由主壳。declare -p variable >>"$tmpf"或者语法${variable@Q}会有用。

    将所有代码写入临时文件后,脚本应发送SIGUSR1到主 shell

    kill -s USR1 "$PPID"
    

    并退出。

  4. 由于陷阱,主 shell 将获取.bash_profile_background_wizard.


笔记

  • 如果信号在主 shell 等待某个同步命令完成时出现,则在命令完成之前陷阱不会被执行

  • 如果您在信号到来之前生成子级bash,则不要指望子级会对信号做出反应。因为:

    • 由于PID不同,它不会收到信号,
    • 它不会继承陷阱。

    bash请注意,如果您以导致其获取源的方式生成子项.bash_profile,那么整个技巧将从该子项开始bash 独立地

  • 如果您在信号到来之前生成一个后台进程,则不要指望当主 shell 最终获取临时文件并可能更改其自己的环境时,该进程的环境会被更新。

  • 如果您在陷阱运行之前退出主 shell,那么临时文件将保留(trap '…' EXIT可能很方便)。


概念证明

以下代码片段旨在粘贴到交互式 Bash 中。这样您就可以测试这个想法,而不会污染您的.bash_profile.其机制与上面介绍的相同。

# imagine this block is sourced from .bash_profile
{
export PS1='poor prompt > '
tmpf="$(mktemp)"
trap '
  . "$tmpf"
  rm "$tmpf"
  trap - USR1
' USR1
# wizard in background
tmpf="$tmpf" bash -c '
  sleep 10  # delay
  PS1="RICH PROMPT >>> "
  declare -p PS1 >>"$tmpf"
  kill -s USR1 "$PPID"
' &
}
# Initially you will see a poor prompt.
# Run few basic commands or keep striking Enter.
# After 10 seconds the prompt should change.

答案2

你正试图将两个不相容的事物结合起来。

source命令使 shell 读取文件并执行其中的命令在当前的shell环境中

启动异步作业,即使用 在后台运行命令&,在其自己的环境中运行该命令,与调用 shell 分开。该环境是父 shell 环境的副本,它将随着命令终止而被销毁。后台作业无法在父 shell 的环境中设置环境变量等。

你的命令的效果

source .bash_profile_load_in_background &

bash启动一个新的 shell 进程(在后台),在其中运行source命令。然后该进程就会终止,修改后的环境也会随之终止。

.bash_profile_load_in_background效果与您单独运行的效果相同脚本(它也在自己的环境中运行)。

有关的:

相关内容