【问题标题】:Cannot start two interactive shells using popen无法使用 popen 启动两个交互式 shell
【发布时间】:2013-02-03 04:55:28
【问题描述】:

我有以下 Python sn-p,但无法解释为什么它的行为方式如此。

import subprocess  
bash1 = subprocess.Popen(["/bin/bash","-l", "-i"], stdin=subprocess.PIPE)  
print "Checkpoint 1"  
bash2 = subprocess.Popen(["/bin/bash","-l", "-i"], stdin=subprocess.PIPE)  
print "Checkpoint 2"  
bash1.communicate("echo 'works1'")  
bash2.communicate("echo 'works2'")  
print "OK"

当我运行它时,我得到以下输出:

[user@localhost ~]$ python test.py  
Checkpoint 1  
Checkpoint 2  
[1]+  Stopped                 python test.py
[user@localhost ~]$ [user@localhost ~]$ echo 'works1'  
works1  
[user@localhost ~]$ logout  
[user@localhost ~]$ fg    
python test.py  
[user@localhost ~]$ echo 'works2'  
works2  
[user@localhost ~]$ logout 
OK  
[user@localhost ~]$
  1. 为什么 Python 进程在第二次 Popen 调用时停止? (被 tty 输入停止),如何避免?
  2. 为什么在 echo 'works1' 完成后会收到注销消息,如何避免?

【问题讨论】:

  • 您是否尝试在通信呼叫结束时添加换行符? bash1.communicate("echo 'works1'\n").
  • 我试过了,结果还是一样。

标签: python bash subprocess popen


【解决方案1】:

问题 1 的答案:

这是因为交互式 bash shell 需要连接到终端(“控制终端”)并获取它以处理作业控制中断(例如 Control-Z)。第二次调用尝试获取终端但无法获取,因此被暂时挂起。

问题 2 的答案:

communicate 将其参数写入子进程的标准输入管道,然后关闭它。 Bash 在其标准输入耗尽时终止(就像在 bash 终端会话中输入 Control-D)。

如果要保持 bash 子进程运行,则直接写入其标准输入,而不是使用communicate,如下所示:

bash1.stdin.write("echo 'works1'\n")

顺便说一句,如果您希望命令实际执行,您确实需要添加换行符。

解决方案:

如果您想运行两个或多个交互式 shell,您应该将每个 shell 的标准输入设置为 pseudo-terminal 而不是子进程 PIPE。

【讨论】:

  • 我不明白为什么 stdin=subprocess.PIPE 不起作用。这是两个独立的管道,对吧?那么为什么第二个不能与第一个共存呢?我不需要通过标准输入发送任何命令,我只需要通过“-c”发送一个命令,但我确实需要一个交互式 shell,因为我希望命令能够使用用户的正常别名和 shell 函数.我还需要一个 pty 吗?
【解决方案2】:

根据isedev 的提示,我打开了2 个伪终端。重要的是主进程(这个python脚本)在主PTY文件上读写,而子子进程使用从文件作为stdin、stdout和stderr。代码的底部只是用于测试一切是如何工作的。

import subprocess
import os
import pty
import select
import time

# according to> http://fleckenzwerg2000.blogspot.com/2011/10/running-and-controlling-gnu-screen-from.html
(master1, slave1) = pty.openpty()
bash1 = subprocess.Popen(["bash", "-l", "-i"], stdin=slave1, stdout=slave1, stderr=slave1)
(master2, slave2) = pty.openpty()
bash2 = subprocess.Popen(["bash", "-l", "-i"], stdin=slave2, stdout=slave2, stderr=slave2)
data = "echo 'bla'\n"

## taken from> http://stackoverflow.com/questions/14564904/how-to-send-tab-key-to-python-subprocesss-stdin
def write_all(masterPTY, data):
    """Successively write all of data into a file-descriptor."""
    while data:
        chars_written = os.write(masterPTY, data)
        data = data[chars_written:]
    return data

def read_all(masterPTY):
    r,w,x = select.select([masterPTY], [], [], 10)
    if r:
        data = os.read(masterPTY, 1024)
        return data


write_all(master1, "echo 'bla1'\n")
write_all(master2, "echo 'bla2'\n")
time.sleep(1)
print read_all(master1)
write_all(master1, "echo 'bla1'\n")
time.sleep(1)

print read_all(master2)

time.sleep(1)

os.close(master1)
os.close(slave1)
os.close(master2)
os.close(slave2)

bash1.terminate()
bash2.terminate()

print "OK"

就是这样。希望它可以帮助某人!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-04
    • 2021-12-04
    • 1970-01-01
    • 2022-07-14
    相关资源
    最近更新 更多