Python:subprocess.stdin.write 无法正常工作

发布于 2024-11-24 03:47:22 字数 3190 浏览 0 评论 0原文

我是 python 新手,但我可以编码和调试一些。在过去的几天里,以下问题一直困扰着我,并寻求答案。非常感谢任何帮助。 任务:我想做一个交互式 telnet(我知道有一个 telnet 库,但由于各种原因我们没有使用它)。 为此,我使用 subprocess.popen

p = subprocess.Popen(telnet_command,
                         stdin = subprocess.PIPE,
                         stdout = outputfileobj,
                         stderr = errorfileobj)

执行 poll() 来查看服务器是否已连接到会话。验证后,我继续写入标准输入以进行交互式通信。

inputTxt = 'GET / HTTP/1.1\nHost: ' + hostheader + '\n\n'
p.stdin.write(inputTxt)
p.stdin.flush()

这就是问题发生的地方。我得到 http 响应(或至少输出)5/6 次,但 1/6 次,我没有得到输出,子进程被终止 - 这是不可能的。

我对失败案例进行了系统跟踪,请在下面找到相同的内容。

16129 write(7, "GET / HTTP/1.1\nHost: www.google."..., 37) = 37
16129 gettimeofday({1310538497, 134474}, NULL) = 0
16129 futex(0x81993a8, FUTEX_WAKE, 1)   = 0
16129 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=56, ...}) = 0
16129 fstat64(4, {st_mode=S_IFREG|0644, st_size=520689, ...}) = 0
16129 _llseek(4, 520689, [520689], SEEK_SET) = 0
16129 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=56, ...}) = 0
16129 write(4, "2011-07-13 06:28:17,134 : pconns"..., 170) = 170
16129 futex(0x81d3b30, FUTEX_WAKE, 1)   = 0
16129 waitpid(16198, 0xffa945e8, WNOHANG) = 0
16129 gettimeofday({1310538497, 135160}, NULL) = 0
16129 fstat64(6, {st_mode=S_IFREG|0644, st_size=81, ...}) = 0
16129 fstat64(6, {st_mode=S_IFREG|0644, st_size=81, ...}) = 0
16129 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf74b9000
16129 _llseek(6, 0, [0], SEEK_SET)      = 0
16129 fstat64(6, {st_mode=S_IFREG|0644, st_size=81, ...}) = 0
16129 _llseek(6, 0, [0], SEEK_CUR)      = 0
16129 read(6, "Trying 74.125.236.48...\nConnecte"..., 4096) = 81
16129 read(6, "", 4096)                 = 0
16129 close(6)                          = 0
16129 munmap(0xf74b9000, 4096)          = 0
16129 gettimeofday({1310538497, 135778}, NULL) = 0
16129 futex(0x81993a8, FUTEX_WAKE, 1)   = 0
16198 <... select resumed> )            = 1 (in [0])
16198 read(0, "GET / HTTP/1.1\nHost: www.google."..., 8191) = 37
16129 stat64("/etc/localtime",  <unfinished ...>
16198 ioctl(1, TCFLSH, 0)               = -1 ENOTTY (Inappropriate ioctl for device)
16129 <... stat64 resumed> {st_mode=S_IFREG|0644, st_size=56, ...}) = 0
16198 select(4, [0 3], [3], [3], {0, 0}) = 1 (out [3], left {0, 0})
16129 fstat64(4, {st_mode=S_IFREG|0644, st_size=520859, ...}) = 0
16198 send(3, "GET / HTTP/1.1\r\nHos\377\363\377\375\6", 24, 0 <unfinished ...>
16129 _llseek(4, 520859,  <unfinished ...>
16198 <... send resumed> )              = 24
16198 select(4, [0 3], [3], [3], {0, 0} <unfinished ...>
16129 <... _llseek resumed> [520859], SEEK_SET) = 0
16198 <... select resumed> )            = 1 (out [3], left {0, 0})
16198 send(3, ": www.google.com\r\n\r\n", 20, 0 <unfinished ...>
16129 stat64("/etc/localtime",  <unfinished ...>
16198 <... send resumed> )              = 20

如果仔细查看跟踪中的行 16198 send(3, "GET / HTTP/1.1\r\nHos\377\363\377\375\6", 24, 0 ,字符串“Host”被一些“Hos\377\363\377\375\6”取代。确定为什么这种情况偶尔会发生,并且这会关闭我建立的 telnet 连接,如果您需要更多数据,请告诉我。

I'm new to python but I can code and debug a bit. The following problem has been on my head for the past couple of days and looking out for an answer. Any help is much appreciated.
Task: I wanted to do an interactive telnet (I know there is an telnet library, but we are not using it for various reasons).
For this purpose, I use a subprocess.popen

p = subprocess.Popen(telnet_command,
                         stdin = subprocess.PIPE,
                         stdout = outputfileobj,
                         stderr = errorfileobj)

I do a poll() to see if the server has got connected to the session. Once I verified, I proceed to write to the stdin for interactive communication.

inputTxt = 'GET / HTTP/1.1\nHost: ' + hostheader + '\n\n'
p.stdin.write(inputTxt)
p.stdin.flush()

Here is where the problem has occurred. I get the http response (or atleast output) for 5/6 times but 1/6 time, I do not get a output and the subprocess gets terminated - which is not possible.

I ran a system trace for the failure case and please find below the same.

16129 write(7, "GET / HTTP/1.1\nHost: www.google."..., 37) = 37
16129 gettimeofday({1310538497, 134474}, NULL) = 0
16129 futex(0x81993a8, FUTEX_WAKE, 1)   = 0
16129 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=56, ...}) = 0
16129 fstat64(4, {st_mode=S_IFREG|0644, st_size=520689, ...}) = 0
16129 _llseek(4, 520689, [520689], SEEK_SET) = 0
16129 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=56, ...}) = 0
16129 write(4, "2011-07-13 06:28:17,134 : pconns"..., 170) = 170
16129 futex(0x81d3b30, FUTEX_WAKE, 1)   = 0
16129 waitpid(16198, 0xffa945e8, WNOHANG) = 0
16129 gettimeofday({1310538497, 135160}, NULL) = 0
16129 fstat64(6, {st_mode=S_IFREG|0644, st_size=81, ...}) = 0
16129 fstat64(6, {st_mode=S_IFREG|0644, st_size=81, ...}) = 0
16129 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf74b9000
16129 _llseek(6, 0, [0], SEEK_SET)      = 0
16129 fstat64(6, {st_mode=S_IFREG|0644, st_size=81, ...}) = 0
16129 _llseek(6, 0, [0], SEEK_CUR)      = 0
16129 read(6, "Trying 74.125.236.48...\nConnecte"..., 4096) = 81
16129 read(6, "", 4096)                 = 0
16129 close(6)                          = 0
16129 munmap(0xf74b9000, 4096)          = 0
16129 gettimeofday({1310538497, 135778}, NULL) = 0
16129 futex(0x81993a8, FUTEX_WAKE, 1)   = 0
16198 <... select resumed> )            = 1 (in [0])
16198 read(0, "GET / HTTP/1.1\nHost: www.google."..., 8191) = 37
16129 stat64("/etc/localtime",  <unfinished ...>
16198 ioctl(1, TCFLSH, 0)               = -1 ENOTTY (Inappropriate ioctl for device)
16129 <... stat64 resumed> {st_mode=S_IFREG|0644, st_size=56, ...}) = 0
16198 select(4, [0 3], [3], [3], {0, 0}) = 1 (out [3], left {0, 0})
16129 fstat64(4, {st_mode=S_IFREG|0644, st_size=520859, ...}) = 0
16198 send(3, "GET / HTTP/1.1\r\nHos\377\363\377\375\6", 24, 0 <unfinished ...>
16129 _llseek(4, 520859,  <unfinished ...>
16198 <... send resumed> )              = 24
16198 select(4, [0 3], [3], [3], {0, 0} <unfinished ...>
16129 <... _llseek resumed> [520859], SEEK_SET) = 0
16198 <... select resumed> )            = 1 (out [3], left {0, 0})
16198 send(3, ": www.google.com\r\n\r\n", 20, 0 <unfinished ...>
16129 stat64("/etc/localtime",  <unfinished ...>
16198 <... send resumed> )              = 20

If you closely look at the line 16198 send(3, "GET / HTTP/1.1\r\nHos\377\363\377\375\6", 24, 0 in the trace, the string "Host" is replaced by some "Hos\377\363\377\375\6". I'm not sure why this occurs once in a while and also this closes the telnet connection I established. Please let me know if you need more data.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

假情假意假温柔 2024-12-01 03:47:22

如果您尝试使用 communicate() 方法而不是使用 stdin 将数据发送到子进程,因为 Python 文档

警告:使用communicate()而不是.stdin.write、.stdout.read或
.stderr.read 以避免由于任何其他操作系统管道而导致的死锁
缓冲区填满并阻塞子进程。

试试这个:

p.communicate(input='GET / HTTP/1.1\nHost: ' + hostheader + '\n\n')

What if you try to use the communicate() method to send data to the subprocess instead of using stdin, as the latter is discouraged by Python's documentation:

Warning: Use communicate() rather than .stdin.write, .stdout.read or
.stderr.read to avoid deadlocks due to any of the other OS pipe
buffers filling up and blocking the child process.

Try this:

p.communicate(input='GET / HTTP/1.1\nHost: ' + hostheader + '\n\n')
拥抱影子 2024-12-01 03:47:22

虽然沟通更好,但如果您确实必须使用stdin.write,您可以使用以下方式使调用非阻塞:

fcntl.fcntl(p.stdout.fileno (), fcntl.F_SETFL, os.O_NONBLOCK)

为了更好地理解,请阅读以下内容:

io非阻塞

Though communicate is better, if you really have to use stdin.write you can make the calls non-blocking using:

fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

for better understanding read this:

io non blocking

夜深人未静 2024-12-01 03:47:22

如果不需要交互,我不会使用communicate,而是提前将文本放入管道中。
我还没有找到更Pythonic的“text_to_stream”。

import os
import sys
import subprocess


def text_to_stream(text):
    p = os.pipe()
    os.write(p[1], text.encode('utf-8'))
    os.close(p[1])
    return os.fdopen(p[0], "r")


def run(cmd, stdin_text=None):
    stdin = text_to_stream(stdin_text) if stdin_text else sys.stdin
    result = subprocess.run(
        cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=stdin)
    result.check_returncode()

If there is no need to interact, I don't use communicate, but put the text into a pipe ahead of time.
I've not found a more pythonic "text_to_stream" yet.

import os
import sys
import subprocess


def text_to_stream(text):
    p = os.pipe()
    os.write(p[1], text.encode('utf-8'))
    os.close(p[1])
    return os.fdopen(p[0], "r")


def run(cmd, stdin_text=None):
    stdin = text_to_stream(stdin_text) if stdin_text else sys.stdin
    result = subprocess.run(
        cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=stdin)
    result.check_returncode()
故事未完 2024-12-01 03:47:22

对于那些真正想要解决所写问题的人来说,这是一个答案 - 当你需要交互式输出时(指的是我五分钟前) - 特别是当 communicate()not

在下面的程序中,我将孩子的标准输出设置为非阻塞(对于标准错误可能也是必要的,具体取决于用例)以允许我阅读。另一种选择可能是查看底层 fd 是否可以使用 select 或朋友读取。

然后我写入孩子的标准输入;并进行显式刷新以确保它消失,然后检查是否有任何东西返回。在此特定示例中,第一次读取返回 None,然后该后的每次读取返回前一个周期的(转换后的)写入数据。

显然,让孩子不将其输出缓冲到无穷大是留给读者的练习。我通过使用 unbuffer 程序对这个玩具示例进行了破解,但这并不适合所有示例。但是,如果发送大量数据,请注意死锁;在写入整个缓冲区之前,您可能必须读取部分结果(此时可能需要将 sp.stdin 设置为非阻塞,或者通常需要更复杂的 I/O 处理)。

当子进程终止时,flush() 得到一个 BrokenPipeError 异常(在我的示例中,另一种可能性是在 write() 中获取该异常) 虽然它不适合我的用例,

但如果您正在做一个真正的交互式应用程序,您需要响应孩子所做的特定事情,那么使用 pexpect 模块可能是正确的答案。

import sys
import os
import subprocess
import time
import fcntl

sp = subprocess.Popen(["timeout", "2", "unbuffer", "-p", "tr", ".", "-"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
fl = fcntl.fcntl(sp.stdout, fcntl.F_GETFL)
fcntl.fcntl(sp.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
while True:
    val = str(time.time()) + "\n"
    x = sp.stdin.write(val.encode("utf-8"))
    sp.stdin.flush()
    y = sp.stdout.read(1024)
    print(f"Wrote {x} bytes of {val[:-1]}, got {y}")
    time.sleep(0.4)

This is an answer for those who actually want to solve the problem as written--when you need interactive output (meaning me five minutes ago)--specifically when communicate() is not the correct answer because you need to provide multiple chunks of input.

In the below program, I set up the child's stdout to be non-blocking (might be necessary for stderr as well, depending on use case) to allow me to read. Another option could be to see if the underlying fd is readable with select or friends.

I then write to the child's stdin; and do an explicit flush to make sure it goes out, then check to see if anything came back. In this specific example, the first read returns None, and then each read after that return's the previous cycle's (transformed) write data.

Obviously making the child not buffer its output unto infinity is left as an exercise for the reader. I hacked it for this toy example by using the unbuffer program, but that is not appropriate for all examples. However, beware of deadlock if you are sending a lot of data; you might have to read partial results before you write the entire buffer (this is where setting sp.stdin to non-blocking might be required, or generally more sophisticated I/O handling).

When the child terminates, the flush() got a BrokenPipeError exception (in my example, another possibility would be getting that in the write().

While it is not appropriate for my use case, if you are doing a true interactive application where you need to respond to specific things the child does, using the pexpect module is probably the correct answer.

import sys
import os
import subprocess
import time
import fcntl

sp = subprocess.Popen(["timeout", "2", "unbuffer", "-p", "tr", ".", "-"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
fl = fcntl.fcntl(sp.stdout, fcntl.F_GETFL)
fcntl.fcntl(sp.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
while True:
    val = str(time.time()) + "\n"
    x = sp.stdin.write(val.encode("utf-8"))
    sp.stdin.flush()
    y = sp.stdout.read(1024)
    print(f"Wrote {x} bytes of {val[:-1]}, got {y}")
    time.sleep(0.4)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文