3

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

#!/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 调用都不会正确执行,并且脚本无需等待即可运行。为什么会这样?

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

编辑:我在 Ubuntu Linux 12.04 上运行它,当我man nc得到 BSD 通用命令手册时,我假设这是 BSD netcat。

4

2 回答 2

3

这里的问题是您要发送SIGINT到流程。如果您只是close,将关闭其套接字并退出stdinnc这就是您想要的。

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

而不是lookup_client.send_signal(subprocess.signal.SIGINT),只是做lookup_client.stdin.close()nc将在其输入中将此视为 EOF,并正常退出,此时您的服务器也将退出。

#!/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"

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

None
None
magic
Lookup server terminated properly

有时第二个None是 a 0,和/或它出现在之后magic而不是之前,但除此之外,它总是四行。(我在 OS X 上运行。)

对于这个简单的案例(尽管可能不是您的真实案例),只需使用communicate而不是尝试手动操作。

#!/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 调用都不会正确执行,并且脚本无需等待即可运行。为什么会这样?

正如文档所说:

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

所以,subprocess.Popen(["nc", "-l", "5050"], shell=True)确实/bin/sh -c 'nc' -l 5050,并且sh不知道如何处理这些论点。

您可能确实想使用一个 args 数组,但是您必须摆脱shell=True- 无论如何这是一个好主意,因为 shell 在这里没有帮助您。

还有一件事:

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

这可能会打印 -2 或 None,这取决于客户端是否已完成响应SIGINT并在您之前被杀死poll。如果您想实际获得-2,则必须调用wait而不是poll(或执行其他操作,例如循环直到poll返回非无)。

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

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).

于 2012-11-20T21:08:13.173 回答
2

你的调用nc很糟糕,如果我在命令行中调用它会发生什么:

# Server window:
[vyktor@grepfruit ~]$ nc -l 5050

# Client Window
[vyktor@grepfruit ~]$ nc localhost 5050
[vyktor@grepfruit ~]$ echo $?
1

这意味着(1in $?)失败。

一旦你使用-p

-p, --local-port=NUM       local port number

NC 开始监听,所以:

# Server window
[vyktor@grepfruit ~]$ nc -l -p 5050
    # Keeps handing

# Client window
[vyktor@grepfruit ~]$ echo Hi | nc localhost 5050
    # Keeps hanging

添加-c到客户端调用后:

-c, --close                close connection on EOF from stdin

你最终会得到这个:

# Client window
[vyktor@grepfruit ~]$ echo Hi | nc localhost 5050 -c
[vyktor@grepfruit ~]$

# Server window
[vyktor@grepfruit ~]$ nc -l -p 5050
Hi
[vyktor@grepfruit ~]$ 

所以你需要这段python代码:

#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l -p 5050", shell=True)
lookup_client = subprocess.Popen("nc -c localhost 5050", shell=True, 
                      stdin=subprocess.PIPE) 
lookup_client.stdin.write("magic\n")
lookup_client.stdin.close()            # This
lookup_client.send_signal(subprocess.signal.SIGINT) # or this kill
lookup_server.wait()
print "Lookup server terminated properly"
于 2012-11-20T21:02:34.060 回答