【问题标题】:Killing a background process while building a bash prompt在构建 bash 提示时终止后台进程
【发布时间】:2018-09-21 04:10:57
【问题描述】:

我已经设法让我的 bash 提示延迟加载一些组件以产生异步渲染的感觉。延迟加载功能作为后台进程运行,(render_async &)

从下面的截屏视频中,您可以看到提示实际上是如何立即加载部分内容以及其他“延迟加载”的。但是,我注意到,如果您在加载异步部分之前更改目录,则新提示将被错误的上下文覆盖。

我解决这个问题的思路如下:

  • 使用jobs 命令检查后台进程
  • 我发现jobs 命令在不在当前工作目录后台的进程旁边显示wd:*
  • 查找并终止所有具有该模式的后台进程以避免这种覆盖

我在异步命令中添加了一个 sleep 来模拟它,我可以看到后台进程挂起。

[1]   Running                 render_async &
[2]-  Running                 render_async &  (wd: ~/Projects/My Personal Space/blog-core)
[3]+  Running                 render_async &

我继续写道:

  jobs | grep 'render_async.*wd:' | cut -d "[" -f2 | cut -d "]" -f1 | while read -r line ; do
    kill "%$line"
  done

理论上应该解析其他目录中后台作业的id并杀死它们。但是在实践中,我不断得到上面的例子kill: %2: no such job

当我在 shell 本身中执行相同的 kill 命令时,它可以完美运行。

在此不胜感激。

其他想法

我尝试命名我的分叉进程(通过将 CWD 附加到函数名称来命名每个异步进程),灵感来自 https://askubuntu.com/questions/153900/how-can-i-start-a-process-with-a-different-name,其中:

bash -c "exec -a MyUniqueProcessName <command> &" 替换 当前的shell,没有创建新进程,这就是我开始的原因 调用 exec 的新 shell。

然后你可以终止进程:

pkill -f MyUniqueProcessName 下可以启动多个进程 相同的名字,然后 pkill -f 将杀死所有的人。

但这一直告诉我,我试图传递给 <command> 的异步函数没有找到,我怀疑它与它是一个自定义函数这一事实有关,并且是一个新的 bash过程,但我不是这里的专家。

谢谢

【问题讨论】:

  • 您使用的终端是否提供标题窗口或其他可以写入状态的带外位置? 改进这种方法肯定是可能的,但我不确定你是否能够使其完全健壮:如果一个新进程认为它拥有终端,但你的旧 shell 的子进程还在打印,冲突在所难免。
  • 感谢@CharlesDuffy 的评论。我正在使用 MacOSX 终端,但很想看看我如何改进它并使其变得强大
  • while 是一个隐含的子shell,据我所知,因此 kill 在此期间可能无法看到作业。
  • @cxw,不,while 不会创建子外壳;在到while 的管道中,负责的是管道,而不是while。 (不过,你是对的,它不会是同一个 shell 实例并且不会有相同的作业表;对于非交互式脚本,可以使用 lastpipe 来更改该行为,但它不适用于作业控制)。
  • ...一般来说,当你从 bash 启动某些东西并想稍后杀死它时,你应该保留 pid:foo & foo_pid=$! 让你稍后 kill "$foo_pid" (并检查它是否仍在运行if kill -0 "$foo_pid" &>/dev/null; then 等)。

标签: bash shell command-prompt background-process kill


【解决方案1】:

管道创建一个隐式子外壳,因此来自父级的作业在运行kill 的管道到while 循环内不可见。这个怎么样,至少会扼杀第一份工作?

job="$(jobs | grep 'render_async.*wd:' | cut -d "[" -f2 | cut -d "]" -f1)"
kill "%$job"

在我的测试中,$(...) 发生在当前 shell 中,至少最初是这样,因此作业表是可见的。示例:

$ cat &
[1] 8296
$ jobs
[1]+  Stopped                 cat
$ echo "$(jobs| cut -d "[" -f2 | cut -d "]" -f1|head -1)"
1

(顺便说一句,你想杀死的工作总是有相同的工作编号吗?你可以硬编码%2,或者不管它可能是什么?)

编辑多任务:

joblist="$(jobs| sed -E 's/^[^0-9]*([0-9]+).*$/\1/'|tr '\n' '@')"
    # E.g., 1@2@
IFS='@'     # Split on @
for job in $joblist     # No double-quotes!
do
    kill "%$job"
done

来自 shell 提示的工作示例:

$ cat&
[1] 8824
$ cat&
[2] 6452

[1]+  Stopped                 cat
$ jobs
[1]-  Stopped                 cat
[2]+  Stopped                 cat
$ joblist="$(jobs| sed -E 's/^[^0-9]*([0-9]+).*$/\1/'|tr '\n' '@')"
$ IFS=@
$ for job in $joblist; do kill "%$job" ; done

[1]-  Stopped                 cat

[2]+  Stopped                 cat
$ jobs
[1]-  Terminated              cat
[2]+  Terminated              cat
$ jobs
$

我选择 @ 作为分隔符,因为我认为它对 shell 没有任何特殊意义。

不知道对PS1功能环境有没有影响。

【讨论】:

  • 工作编号是动态的,可以改变..我现在试试你的方法:)
  • 即使您采用第一种方法,我仍然得到-bash: kill: %: no such job。我不确定我将其作为我的 PS1 函数执行这一事实与它有什么关系
  • 好吧,很有趣.. 似乎问题不在于我硬编码kill %2 时无法访问作业表,它起作用并且作业被终止了。动态地这样做似乎是一个问题
  • @AhmadAssaf 编辑了多作业支持。除非多作业方式有效,否则请您使用 MCVE 更新您的问题?
  • 使用 for 的技巧完成了工作@cxw。似乎当时的子进程是cuplrit。但是,我现在遇到了另一个问题。为了抑制后台作业的状态,我运行它时用括号(render_async &) 包裹。当我这样做时,杀死并没有发生,但是当我删除它们时,一切都按预期工作。知道为什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-05
  • 2014-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-05
相关资源
最近更新 更多