Python子进程输出不正确?

时间:2012-11-20 20:38:23

标签: python subprocess netcat

我认为我根本不正确理解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。

2 个答案:

答案 0 :(得分: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代替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调用正确执行,并且脚本会在没有等待的情况下运行。为什么会这样?

正如the docs所说:

  

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

因此,subprocess.Popen(["nc", "-l", "5050"], shell=True)执行/bin/sh -c 'nc' -l 5050sh不知道如何处理这些参数。

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

还有一件事:

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

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

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

你可以在time.sleep(5)来电之前投入signal进行测试 - 但显然这不是真正的修复,甚至是可接受的黑客攻击;它只对测试问题有用。你需要做的就是杀死客户端,直到它完成了你想做的一切。对于复杂的情况,你需要建立一些机制来做到这一点(例如,阅读它的标准输出),而对于简单的情况,communicate已经是你需要的一切(并且没有理由在第一次杀死孩子位)。

答案 1 :(得分:2)

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

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

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

这意味着(1)失败中的$?

使用-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"