![从 bash_profile 在后台获取长时间运行的脚本](https://linux22.com/image/194229/%E4%BB%8E%20bash_profile%20%E5%9C%A8%E5%90%8E%E5%8F%B0%E8%8E%B7%E5%8F%96%E9%95%BF%E6%97%B6%E9%97%B4%E8%BF%90%E8%A1%8C%E7%9A%84%E8%84%9A%E6%9C%AC.png)
.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
应该如何工作。
想法是这样的:
让我们
.bash_profile
创建一个临时文件并记住它的路径:tmpf="$(mktemp)"
(
mktemp
不可携带。)设置 a
trap
,以便 shell 将临时文件作为源SIGUSR1
。获取后,不再需要文件和陷阱,因此请考虑将清理作为陷阱的一部分。trap ' . "$tmpf" rm "$tmpf" trap - USR1 ' USR1
.bash_profile_background_wizard
在后台运行。该脚本必须能够写入临时文件,因此给它一个描述符或路径(作为参数或在环境中)。例子:tmpf="$tmpf" ~/.bash_profile_background_wizard &
该脚本应启动长时间运行的任务并将 shell 代码写入临时文件。它应该建造函数和变量的定义、依赖于长时间运行的任务的任何内容、您希望主 shell 在准备就绪时“采用”的任何内容。根据您想要做什么,编写脚本的脚本可能并不容易。记住临时文件即将解析的由主壳。
declare -p variable >>"$tmpf"
或者语法${variable@Q}
会有用。将所有代码写入临时文件后,脚本应发送
SIGUSR1
到主 shellkill -s USR1 "$PPID"
并退出。
由于陷阱,主 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
效果与您单独运行的效果相同脚本(它也在自己的环境中运行)。
有关的: