“bash:no job control in this shell” 是什么意思?

89

我认为这与父进程创建新的子进程有关,并且没有tty。有人可以解释一下底层的细节吗?例如,bash、进程创建等相关的工作模型?

这可能是一个非常广泛的话题,所以指向帖子的指针也非常受欢迎。我已经谷歌搜索了一段时间,所有的结果都是关于非常具体的情况,没有涉及场景背后的故事。为了提供更多上下文,以下是导致'bash: no job control in this 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: no job control in this shell”的来源:

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


2
我无法从你的问题中确定你是想要关于shell中作业控制的一般信息,还是想要解释为什么在某些情况下作业控制不可用。 - kojiro
两者都可以 :) 这可能是一个非常广泛的话题,因此对帖子的指针也非常感激。我已经谷歌了一段时间,所有的结果都是关于非常具体的情况,没有一个是关于幕后故事的。 - luanjunyi
你为什么要启动一个新的 shell 来运行 SSH?从命令行运行脚本并不会给我“无作业控制”,虽然它在执行 SSH 时会挂起,这实际上是预期的。因此,在技术上,“else” 永远不会被触发。你从哪里运行这个脚本?crontab? - favoretti
2
@luanjunyi,既然你问了,这是我最喜欢的关于shell进程管理主题的文章链接:http://mywiki.wooledge.org/ProcessManagement。 - kojiro
5个回答

114

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

#! /bin/bash   
set -m

22
“什么?为什么不是默认设置?” - Sapphire_Brick
6
在Docker容器中表现出色!为容器的入口点执行后台作业,然后将其重新启动。向不支持从文件进行填充的数据库提供数据... - Marcello DeSales
2
谢谢,但请解释一下“作业控制”是什么意思,以回答“What does "bash:no job control in this shell” mean?”这个问题。 - Jason Doucette
1
我相信同一讨论串中的另一个答案已经提供了解释: https://dev59.com/Rmgt5IYBdhLWcg3wxAVj#11824420 - Marian
1
@MarcellodeSales 很有趣,我正试图解决那个确切的问题,结果导致我找到了这个问题。 - rishabh
显示剩余3条评论

71

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

一个作业可以是单个命令或者一个管道。如果你运行ls,那就是一个作业。如果你运行ls|more,这仍然只是一个作业。如果你运行的命令启动了自己的子进程,则它们也将属于同一个作业,除非它们被故意分离。

没有作业控制,你可以通过在命令行中添加&将一个作业放到后台。这就是你所拥有的全部控制能力。

有了作业控制,你还可以:

  1. 使用CtrlZ暂停正在运行的前台作业
  2. 使用fg恢复挂起的作业到前台
  3. 使用bg恢复挂起的作业到后台
  4. 使用fg将正在运行的后台作业移到前台

Shell维护一个作业列表,你可以通过运行jobs命令来查看。每个作业都被分配一个作业号(不同于组成作业的进程的PID)。你可以使用作业号,加上%作为fgbg的参数来选择要前台或后台运行的作业。%jobnumber标记也可以用于shell内置的kill命令。这很方便,因为作业号从1开始分配,所以它们比PID短。

还有一些快捷方式:%+表示最近运行的前台作业,%-表示之前运行的前台作业,这样你就可以通过CtrlZ然后fg %-(暂停当前作业,恢复另一个作业)之间快速切换两个作业,而不必记住编号。或者你可以使用命令本身的开头。如果你挂起了一个ffmpeg命令,恢复它只需要输入fg %ff(假设没有其他以“ff”开头的活动作业)。最后一个快捷方式是,你不必键入fg。只需输入%-作为命令即可将上一个作业移到前台。

“但我们为什么需要这个?”我能听到你在问。“如果我想运行另一个命令,我可以只启动另一个shell。”确实,有很多种多任务处理的方式。在正常的一天里,我会在tty1到tty10上运行登录shell(是的,还有更多,你只需要激活它们中的一个),其中之一将运行具有4个屏幕的screen会话,另一个可能有一个ssh,在其中远程机器上运行另一个screen会话,再加上我的X会话和3或4个xterm。即使这样,我仍然使用作业控制。

如果我正在进行vilessaptitude或其他任何交互式操作,并且需要运行几个快速命令来决定如何继续,CtrlZ,运行命令,fg就是自然而

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

这很令人困惑。我无法弄清楚为什么ssh命令被传递给一个单独的shell而不是直接从主脚本执行,更不用说为什么有人添加了-i选项。 -i选项告诉shell以交互方式运行,并激活作业控制(等等)。但实际上并没有交互使用它。无论单独的shell和-i背后的目的是什么,关于作业控制的警告都是一个副作用。我猜这是一种解决ssh某些不良功能的黑客方法。这就是当您这样做时应该进行注释的类型。


感谢@Alan。此脚本的意图是持续监测SSH隧道是否打开,如果没有打开,则创建一个新的。我的浏览器使用此隧道作为Socks代理。我并不是一个熟练的bash编码者。整个脚本都是凭试错建立的。我使用“bash -i”希望启动ssh命令类似于fork而不是exec,因为原始监测过程将停止,请用更好的解决方案纠正我。目前,除了生成“无作业控制”消息外,此脚本运行正常。 - luanjunyi
1
除了不打印“无作业控制警告”之外,当您只有ssh -D 7070 -N user@my-ssh.example.com > /dev/null一行时,没有bash -i -c,它还做了什么不同的事情? - Alan Curry
我不小心在一个命令前面留下了一个“%”(这是从Windows批处理文件命令转换而来的...); 正如你所说,百分号转换为fg,因此这个错误就产生了。 - Oliver Zendel
我执行了 sudo su - vuXXXX 命令,然后收到了那个消息。我只能按一次 CTRL+C,之后就不能再按了。 - Roland
写得很好,阅读起来很有趣。谢谢! - resi

26

其中一个可能的选项是无法访问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代码,则可以获取源tar包,在其中您将找到job.c - 这个文件将为您解释更多关于“底层”的内容。


4

我在自己的嵌入式系统上遇到了问题,并通过使用"setsid"运行getty进程来解决了"无作业控制"错误,根据其man页面的说明,它启动一个具有新会话标识符的进程。


7
我认为这并没有真正回答问题,最多只能作为一条评论。 - meskobalazs
1
@meskobalazs 不,我认为这实际上是我遇到的问题的答案,它给出了相同的错误信息。 - Smar

2

我面临这个问题只是因为我把以前执行的带有%前缀的命令与zsh一起误复制了,比如说% echo this而不是echo this。对于这种愚蠢的打字错误,错误信息非常不清楚。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接