Python 子进程输出不正确? [英] Python subprocesses don't output properly?

查看:44
本文介绍了Python 子进程输出不正确?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为我根本没有正确理解 python 子进程,但这里有一个简单的例子来说明我感到困惑的一点:

I don't think I'm understanding python subprocess properly at all but here's a simple example to illustrate a point I'm confused about:

#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l 5050", shell=True)
lookup_client = subprocess.Popen("nc localhost 5050", shell=True, stdin=subprocess.PIPE) 
print lookup_client.poll()
lookup_client.stdin.write("magic\n")
print lookup_client.poll()                                                                        
lookup_client.send_signal(subprocess.signal.SIGINT)
print lookup_client.poll()
lookup_server.wait()
print "Lookup server terminated properly"

输出返回为

None
None
None

并且永远不会完成.为什么是这样?此外,如果我将 Popen 的第一个参数更改为所有这些参数的数组,则没有一个 nc 调用正确执行并且脚本运行而无需等待.为什么会这样?

and never completes. Why is this? Also, if I change the first argument of Popen to an array of all of those arguments, none of the nc calls execute properly and the script runs through without ever waiting. Why does that happen?

最终,我在一个更大的程序中遇到了一个问题,该程序使用 netcat 和另一个本地运行的程序而不是两个版本的 nc 执行类似的操作.无论哪种方式,我都无法正确写入或读取它们.但是,当我在 python 控制台中运行它们时,一切都按我的预期运行.这一切都让我非常沮丧.如果您有任何见解,请告诉我!

Ultimately, I'm running into a problem in a much larger program that does something similar using netcat and another program running locally instead of two versions of nc. Either way, I haven't been able to write to or read from them properly. However, when I run them in the python console everything runs as I would expect. All this has me very frustrated. Let me know if you have any insights!

我在 Ubuntu Linux 12.04 上运行它,当我 man nc 时,我得到了 BSD 通用命令手册,所以我假设这是 BSD netcat.

I'm running this on Ubuntu Linux 12.04, when I man nc, I get the BSD General Commands manual so I'm assuming this is BSD netcat.

推荐答案

这里的问题是您正在向进程发送 SIGINT.如果您只是close stdinnc 将关闭它的套接字并退出,这正是您想要的.

The problem here is that you're sending SIGINT to the process. If you just close the stdin, nc will close its socket and quit, which is what you want.

听起来您实际上在实际程序中为客户端(尽管不是服务器)使用了 nc,这意味着您有两个简单的修复方法:

It sounds like you're actually using nc for the client (although not the server) in your real program, which means you have two easy fixes:

代替lookup_client.send_signal(subprocess.signal.SIGINT),只需执行lookup_client.stdin.close().nc 会将其视为输入的 EOF,然后正常退出,此时您的服务器也将退出.

Instead of lookup_client.send_signal(subprocess.signal.SIGINT), just do lookup_client.stdin.close(). nc will see this as an EOF on its input, and exit normally, at which point your server will also exit.

#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l 5050", shell=True)
lookup_client = subprocess.Popen("nc localhost 5050", shell=True, stdin=subprocess.PIPE) 
print lookup_client.poll()
lookup_client.stdin.write("magic\n")
lookup_client.stdin.close()
print lookup_client.poll()
lookup_server.wait()
print "Lookup server terminated properly"

当我运行这个时,最常见的输出是:

When I run this, the most common output is:

None
None
magic
Lookup server terminated properly

偶尔第二个 None 是一个 0,和/或它出现在 magic 之后而不是之前,但除此之外,它总是全部四行.(我在 OS X 上运行.)

Occasionally the second None is a 0 instead, and/or it comes after magic instead of before, but otherwise, it's always all four lines. (I'm running on OS X.)

对于这个简单的案例(虽然可能不是你的真实案例),只需使用 communicate 而不是尝试手动进行.

For this simple case (although maybe not your real case), just use communicate instead of trying to do it manually.

#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l 5050", shell=True)
lookup_client = subprocess.Popen("nc localhost 5050", shell=True, stdin=subprocess.PIPE) 
print lookup_client.communicate("magic\n")
lookup_server.wait()
print "Lookup server terminated properly"

同时:

此外,如果我将 Popen 的第一个参数更改为所有这些参数的数组,则所有 nc 调用都不会正确执行,脚本会运行而无需等待.为什么会这样?

Also, if I change the first argument of Popen to an array of all of those arguments, none of the nc calls execute properly and the script runs through without ever waiting. Why does that happen?

正如文档所说:

在带有 shell=True 的 Unix 上……如果 args 是一个序列,则第一项指定命令字符串,任何附加项都将被视为 shell 本身的附加参数.

On Unix with shell=True… If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself.

所以,subprocess.Popen(["nc", "-l", "5050"], shell=True)/bin/sh -c 'nc' -l5050,而 sh 不知道如何处理这些参数.

So, subprocess.Popen(["nc", "-l", "5050"], shell=True) does /bin/sh -c 'nc' -l 5050, and sh doesn't know what to do with those arguments.

您可能确实想要使用参数数组,但随后您必须摆脱shell=True——无论如何这是个好主意,因为shell在这里没有帮助你.

You probably do want to use an array of args, but then you have to get rid of shell=True—which is a good idea anyway, because the shell isn't helping you here.

还有一件事:

lookup_client.send_signal(subprocess.signal.SIGINT)
print lookup_client.poll()

这可能会打印 -2 或 None,这取决于客户端是否已完成对 SIGINT 的响应并在您 poll 之前被杀死.如果你想真正得到 -2,你必须调用 wait 而不是 poll(或者做其他事情,比如循环直到 poll 返回非无).

This may print either -2 or None, depending on whether the client has finished responding to the SIGINT and been killed before you poll it. If you want to actually get that -2, you have to call wait rather than poll (or do something else, like loop until poll returns non-None).

最后,为什么你的原始代码不起作用?好吧,发送 SIGINT 是异步的;无法保证何时生效.举一个可能出错的例子,它可能在客户端打开套接字之前生效,在这种情况下,服务器仍然坐在那里等待一个从未出现的客户端.

Finally, why didn't your original code work? Well, sending SIGINT is asynchronous; there's no guarantee as to when it might take effect. For one example of what could go wrong, it could take effect before the client even opens the socket, in which case the server is still sitting around waiting for a client that never shows up.

你可以在 signal 调用之前加入一个 time.sleep(5) 来测试这个——但显然这不是一个真正的修复,甚至不是一个可以接受的黑客;它只对测试问题有用.您需要做的不是杀死客户端,直到它完成了您希望它做的所有事情.对于复杂的情况,你需要建立一些机制来做到这一点(例如,读取它的标准输出),而对于简单的情况,communicate 已经是你需要的一切(并且没有理由杀死孩子首先).

You can throw in a time.sleep(5) before the signal call to test this—but obviously that's not a real fix, or even an acceptable hack; it's only useful for testing the problem. What you need to do is not kill the client until it's done everything you want it to do. For complex cases, you'll need to build some mechanism to do that (e.g., reading its stdout), while for simple cases, communicate is already everything you need (and there's no reason to kill the child in the first place).

这篇关于Python 子进程输出不正确?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆