http://docs.python.org/library/pty.html说 -
pty.fork()¶ 叉子。将孩子的控制终端连接到伪终端。返回值为(pid,fd)。请注意,子项获取pid 0,并且fd无效。父级的返回值是子级的pid,fd是连接到子级控制终端的文件描述符(以及子级的标准输入和输出)。
这是什么意思?每个进程都有3个fd(stdin,stdout,stderr)。 这会影响这些fds吗?子进程是否会有这些fds?我很困惑.--完全。
答案 0 :(得分:13)
我想我终于得到了Python中pty.fork
的一个最小例子 - 因为我发现找到一个类似的例子非常困难,我在这里发布它是为了解释@ joni的答案。它基本上基于:
特别讨厌的位是找到仍然引用过时的master_open()
的文档;以及pty.fork
将不生成子进程的事实,除非文件描述符(由fork方法返回)由父进程读取! (请注意,在os.fork
中没有此类要求)此外,os.fork
似乎更具可移植性(请阅读一些评论,注意pty.fork
没有在某些平台上工作。
无论如何,这里首先是一个充当可执行文件的脚本(pyecho.py
)(它只是从标准输入读取行,然后以大写形式写回来):
#!/usr/bin/env python
# pyecho.py
import sys;
print "pyecho starting..."
while True:
print sys.stdin.readline().upper()
...然后,这是实际的脚本(它将要求pyecho.py位于同一目录中):
#!/usr/bin/env python
import sys
import os
import time
import pty
def my_pty_fork():
# fork this script
try:
( child_pid, fd ) = pty.fork() # OK
#~ child_pid, fd = os.forkpty() # OK
except OSError as e:
print str(e)
#~ print "%d - %d" % (fd, child_pid)
# NOTE - unlike OS fork; in pty fork we MUST use the fd variable
# somewhere (i.e. in parent process; it does not exist for child)
# ... actually, we must READ from fd in parent process...
# if we don't - child process will never be spawned!
if child_pid == 0:
print "In Child Process: PID# %s" % os.getpid()
# note: fd for child is invalid (-1) for pty fork!
#~ print "%d - %d" % (fd, child_pid)
# the os.exec replaces the child process
sys.stdout.flush()
try:
#Note: "the first of these arguments is passed to the new program as its own name"
# so:: "python": actual executable; "ThePythonProgram": name of executable in process list (`ps axf`); "pyecho.py": first argument to executable..
os.execlp("python","ThePythonProgram","pyecho.py")
except:
print "Cannot spawn execlp..."
else:
print "In Parent Process: PID# %s" % os.getpid()
# MUST read from fd; else no spawn of child!
print os.read(fd, 100) # in fact, this line prints out the "In Child Process..." sentence above!
os.write(fd,"message one\n")
print os.read(fd, 100) # message one
time.sleep(2)
os.write(fd,"message two\n")
print os.read(fd, 10000) # pyecho starting...\n MESSAGE ONE
time.sleep(2)
print os.read(fd, 10000) # message two \n MESSAGE TWO
# uncomment to lock (can exit with Ctrl-C)
#~ while True:
#~ print os.read(fd, 10000)
if __name__ == "__main__":
my_pty_fork()
嗯,希望这有助于某人,
干杯!
答案 1 :(得分:3)
使用pty.fork()的要点是返回的伪终端(pty)文件描述符可用于以不同的方式与生成的进程通信,即。通过直接写入和读取(伪)终端 - 而不是stdin / out / err。
还有更多信息about pty's and tty's(来自StackOverflow)和a link to a simple example of using pty.fork()。
答案 2 :(得分:2)
“和fd是连接到孩子控制终端的文件描述符” - >子进程看不到任何区别,它将能够正常访问stdin / out(我不知道stderr)。唯一的区别是“管道”的另一端不是用户正在读/打字的终端,但可以访问的父进程是返回的fd。
答案 3 :(得分:2)
使用pty.fork()
时,子进程被告知正在写到实际的终端tty,就像您通常使用的那样。但是,它是在写pty,即伪指令,终端,它是由另一个程序控制的tty。
只有一个fd,因为子程序正在将其写入终端。这是stdout,stderr和任何终端转义码的组合。在这种情况下, stdout / stderr没有任何意义,因为它们被打印到终端上,并且当程序连接到pty时它们是不可单独访问的(就像当您阅读程序的输出时您无法分辨)哪个流。)
如果需要,您仍然可以将stdout或stderr重定向到文件。这将在孩子运行的代码的分叉部分中完成。您可以重定向其标准流或重定向子流程的流。
这是一个基于sdaau答案的示例程序(他们的答案在Python3中不起作用)。
#!/usr/bin/env python3
import sys
import os
import time
import pty
import subprocess
def log(chars):
sys.stdout.write(" > " + chars + "\n")
def main():
# fork this script such that a child process writes to a pty that is
# controlled or "spied on" by the parent process
(child_pid, fd) = pty.fork()
# A new child process has been spawned and is continuing from here.
# The original parent process is also continuing from here.
# They have "forked".
if child_pid == 0:
log("This is the child process fork, pid %s" % os.getpid())
log("Child process will run a subprocess controlled by the parent process")
log("All output, including this text, will be written to a pty and handled ")
log("by the parent process.")
# redirect stdout/stderr if you want to here
subprocess.run(["bash"])
else:
log("This is the parent process fork, pid %s" % os.getpid())
log("the fd being read from, %s, is not stdout nor stderr; it is " % fd)
log("simply what the child is trying to write to its tty. ")
log("stdout/stderr are combined along with terminal escape codes.")
print()
# Read initial output of child process before "typing" anything in its pty
sys.stdout.write(os.read(fd, 1024).decode())
print()
# Run any bash commands you want. I/O to the fd is handled as if you are typing
# at a terminal.
os.write(fd, "ls\n".encode())
os.write(fd, "which git\n".encode())
# you can even test tab completions
os.write(fd, "git sta\t\t".encode())
while True:
log("parent will read 1024 bytes that the child wrote to its pty")
log("if no new output is available, parent will wait. Exit with ctrl+c.\n")
# take out decode() to see raw bytes the child wrote to its pty
sys.stdout.write(os.read(fd, 1024).decode())
time.sleep(1)
if __name__ == "__main__":
main()
答案 4 :(得分:0)
谢谢Joni.Here是我理解的。当pty.fork()被调用时。父进程连接到ptmx master。 parent将等待来自键盘的输入或来自master的数据。
孩子关闭了stdin,stdout和stderr。并重复奴隶stdin,stdout.stderr。 现在孩子执行了一个程序(比如说bc)。当你输入1 + 1时,程序正在等待输入 - 它被子/从传递给master(记住child和slave都有一些stdin,stdout,stderr)。 master计算其答案“2”并写入stdout-因为父级正在等待来自master的数据-it选择“2”并写入stdout。我在伪终端上经历了几个很好的旧c程序后得出了这个结论:)我不认为python的逻辑与它们没有什么不同。 HTH某人。