69

我认为它与创建新子进程的父进程有关,并且没有 tty。谁能解释引擎盖下的细节?即bash、进程创建等相关的工作模型?

这可能是一个非常广泛的主题,因此也非常感谢指向帖子的指针。我用谷歌搜索了一段时间,所有结果都是关于非常具体的案例,没有一个是关于幕后故事的。为了提供更多上下文,下面是导致“bash:此 shell 中没有作业控制”的 shell 脚本。

#! /bin/bash

while [ 1 ]; do
    st=$(netstat -an |grep 7070 |grep LISTEN -o | uniq)
    if [ -z $st ]; then
        echo "need to start proxy @$(date)"
        bash -i -c "ssh -D 7070 -N user@my-ssh.example.com > /dev/null"
    else
        echo "proxy OK @$(date)"
    fi
    sleep 3
done

这一行:

bash -i -c "ssh -D 7070 -N user@my-ssh.example.com > /dev/null"

是“bash:no job control in this shell”的来源。

4

5 回答 5

90

您可能需要启用作业控制:

#! /bin/bash   
set -m
于 2017-10-19T11:55:08.823 回答
54

作业控制是 shell 和 tty 驱动程序中的一组功能,允许用户从单个交互式 shell 管理多个作业。

作业是单个命令或管道。如果你跑步ls,那是一份工作。如果你跑步ls|more,那仍然只是一份工作。如果您运行的命令启动了它自己的子进程,那么它们也将属于同一个作业,除非它们被有意分离。

&如果没有作业控制,您可以通过添加到命令行将作业置于后台。这就是您拥有的所有控制权。

通过作业控制,您还可以:

  1. 暂停正在运行的前台作业CtrlZ
  2. 在前台恢复暂停的作业fg
  3. 在后台恢复暂停作业bg
  4. 将正在运行的后台作业带入前台fg

shell 维护着一个作业列表,您可以通过运行该jobs命令查看这些作业。每个都分配有一个作业编号(与组成作业的进程的 PID 不同)。您可以使用前缀为 的作业编号%作为参数,fgbg将作业选择到前台或后台。shell 的内置kill命令也可以使用 %jobnumber 表示法。这很方便,因为作业编号从 1 开始分配,因此它们比 PID 短。

%+最近前台作业和先前前台作业也有快捷方式%-,因此您可以在两个作业之间快速来回切换CtrlZfg %-暂停当前作业,恢复另一作业),而无需记住数字。或者您可以使用命令本身的开头。如果您暂停了一个ffmpeg命令,恢复它就像fg %ff(假设没有其他活动作业以“ff”开头)一样简单。作为最后一个快捷方式,您不必键入fg. 只需%-作为命令输入即可将前一个作业置于前台。

“但我们为什么需要这个?” 我能听到你在问。“如果我想运行另一个命令,我可以启动另一个 shell。” 诚然,多任务处理方式有很多种。在正常的一天,我在 tty1 到 tty10 上运行登录 shell(是的,超过 6 个,您只需要激活它们),其中一个将运行一个包含 4 个屏幕的屏幕会话,另一个可能有一个 ssh 正在运行在它上面有另一个在远程机器上运行的屏幕会话,加上我的带有 3 或 4 个 xterms 的 X 会话。我仍然使用工作控制。

如果我在vior lessor aptitudeor 任何其他交互的事情中,我需要运行几个其他的快速命令来决定如何继续CtrlZ,运行命令,并且fg是自然而快速的。(在很多情况下,交互式程序都有一个!键绑定来为您运行外部命令;我认为这不太好,因为您没有从 shell 的历史记录、命令行编辑器和完成系统中受益。)我每当我看到有人启动辅助 xterm/screen/whatever 来运行一个命令时,我都会感到难过,看两秒钟,然后退出。

现在关于你的这个脚本。总的来说,它似乎没有写得很好。有问题的行:

bash -i -c "ssh -D 7070 -N user@my-ssh.example.com > /dev/null"

令人困惑。我不明白为什么 ssh 命令被传递到一个单独的 shell 而不是直接从主脚本执行,更不用说为什么有人添加-i它了。该-i选项告诉 shell 以交互方式运行,从而激活作业控制(除其他外)。但它实际上并没有被交互式使用。无论单独的 shell 和 . 背后的目的是什么-i,关于作业控制的警告都是副作用。我猜想绕过 ssh 的一些不良功能是一种黑客行为。这就是当你这样做时,你应该评论它的那种事情。

于 2012-08-06T07:54:15.290 回答
23

可能的选项之一是无法访问 tty。

引擎盖下:

  1. bash 检查会话是否是交互式的,如果不是 - 没有作业控制。
  2. 如果设置了forced_interactive,则跳过检查stderr是否附加到tty并再次检查bash是否可以打开/dev/tty以进行读写访问。
  3. 然后检查是否使用了新的生产线规则,如果没有,则作业控制也被禁用。
  4. 如果(且仅当)我们只是将我们的进程组设置为我们的 pid,从而成为进程组负责人,并且终端与我们的(新)进程组不在同一个进程组中,那么将终端的进程组设置为我们的 (新)进程组。如果失败,请将我们的进程组设置回原来的状态(这样我们仍然可以从终端读取)并关闭作业控制。
  5. 如果以上所有方法都失败了,您会看到该消息。

我部分引用了 bash 源代码中的注释。


根据问题作者的附加要求:

http://tiswww.case.edu/php/chet/bash/bashtop.html在这里你可以找到 bash 本身。

如果您可以阅读 C 代码,获取源 tarball,您会在其中找到job.c- 它将向您解释更多“幕后”的东西。

于 2012-08-06T00:56:29.910 回答
4

我在自己的嵌入式系统上遇到了一个问题,我通过使用“setsid”运行 getty 进程摆脱了“无作业控制”错误,根据其手册页,该进程使用新的会话 ID 启动一个进程。

于 2015-02-10T13:08:39.640 回答
0

遇到这个问题只是因为我将先前执行的命令连同%前缀一起错误地复制到 zsh 中,比如% echo this而不是echo this. 对于这样一个愚蠢的错字,这个错误非常不清楚

于 2021-12-01T16:50:57.567 回答