在后台冻结stdin,在前景时解冻它

时间:2015-09-16 00:57:35

标签: python bash shell

我正在尝试在后台运行:

nohup script.py > out 2> err < /dev/null &

脚本(Python 3.4)在某些时候会这样做:

answer = input('? ')

(它有一个在其中一个主题中运行的菜单)

nohup电话正在崩溃:

EOFError: EOF when reading a line

由于我想象/dev/null重定向stdin。如果我在没有stdin重定向的情况下运行它:

nohup script.py > out 2> err &

它崩溃了:

OSError: [Errno 9] Bad file descriptor

如果我用:

运行它
script.py > out 2> err

它可以工作,但阻止我的终端(它在前台)

如果我用:

运行它
script.py > out 2> err &

它在后台运行正常,但一旦达到input来电就会停止。

我想要的是:

  • 能够将stdoutstderr重定向到文件系统
  • 能够将脚本放在后台
  • 能够移动到前台并正常与菜单交互(因此必须以某种方式启用stdin)。 stdoutstderr仍会被重定向到文件系统,但stdin会正常运行。
  • 脚本必须在后台和前台运行正常(当然,菜单不在后台运行,因为stdin已“冻结”)

基本上,我想要的是,当它在后台时,stdin有点“冻结”,无论什么时候到达前景,它都能正常工作。

这可能吗?该解决方案不需要涉及nohup

1 个答案:

答案 0 :(得分:1)

使用交互式菜单,您想要的(以及input如何在python下EOF上工作和失败)意味着在调用程序时无法安全地将文件作为stdin传递。这意味着您唯一的选择就是调用它:

$ script.py > out 2> err &

作为演示,这是我的剧本:

from time import sleep
import sys

c = 0
while True:
    sleep(0.001)
    c += 1
    if c % 1000 == 0:
        print(c, flush=True)
    if c % 2000 == 0:
        print(c, file=sys.stderr, flush=True)
    if c % 10000 == 0:
        answer = input('? ')
        print('The answer is %s' % answer, flush=True)

基本上,每隔一秒它会写入stdout,每两秒钟写一次stderr,最后,每隔十秒它会等待输入。如果我要运行它并等待一段时间(以允许磁盘刷新),并将其链接在一起,如下所示:

$ python script.py > out 2> err & sleep 2.5; cat out err
[1] 32123
1000
2000
2000
$ 

等待至少10秒钟再次尝试cat out err

$ cat out err
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
? 2000
4000
6000
8000
10000

[1]+  Stopped                 python script.py > out 2> err
$ 

请注意,input生成的提示也会写入stdout,程序会有效地继续运行到期望stdin为其提供数据的位置。您只需通过%将流程重新置于前台,然后开始向其提供所需数据,然后暂停^Z Ctrl Z 并使用%&在后​​台再次运行它。例如:

$ %
python script.py > out 2> err
Test input
^Z
[1]+  Stopped                 python script.py > out 2> err
$ %&
[1]+ python script.py > out 2> err &
$

再等一下,等了十秒钟后再次cat out

$ cat out
1000
...
10000
? The answer is Test input
11000
...
20000
? 
[1]+  Stopped                 python script.py > out 2> err
$

这本质上是标准流程通常在前台和后台运行的基本速成课程,如果代码正确处理标准IO,事情就会按预期工作。

最后,你无法双管齐下。如果应用程序需要stdin并且没有提供,则clear选项失败。如果提供了一个,但是应用程序被发送到后台并继续运行,它将是Stopped,因为它期望进一步输入。如果这种停止的行为是不必要的,那么应用程序就会出错,但是当使用EOF作为{/dev/null执行时遇到stdin时,没有什么可以做,只是将应用程序更改为不会导致错误1}}。如果您希望保持stdin不变,应用程序能够以某种方式在后台运行时继续运行,则无法使用input函数,因为它会在stdin为空时阻止(导致进程停止)。

现在您已通过下面的评论澄清了您的“交互式提示”正在线程中运行,并且由于input的使用直接从stdin读取,您似乎不愿意修改您的程序(你要求一般情况)但是期望一个实用程序为你做这个,简单的解决方案是在tmuxscreen会话中执行它,因为它们完全实现了一个独立于任何一个控制台的伪tty已启动(因此您可以断开连接并将会话发送到后台,或启动其他虚拟会话,请参阅手册页),这将提供程序所需的stdio。

最后,如果你真的希望你的应用程序本地支持这个,你不能简单地使用input,但你应该检查是否可以安全地调用input(即可能使用{{} 3}}),或通过检查进程当前是在前台还是后台(您可以开始工作的示例是select,尽管您可能想要使用sys.stdin进行检查,也许。)确定是否可以安全地调用input(但是如果用户在输入时挂起任务,它仍会在input等待时挂起),或使用unix套接字进行通信。