无法理解Python中Popen函数的stdin和stdout如何工作
你好,我是Python的初学者,喜欢通过Python的subprocess模块来执行命令行指令。不过我对Popen构造函数有一些疑问,特别是关于stdin和stdout这两个参数。
假设我正在写一个脚本,用来通过命令行指令ping一个网站,比如“www.google.com”。
程序 #1:
import sys
import subprocess
cmdping = "ping -c4 www.google.com"
p = subprocess.Popen(cmdping,shell=True,stdout=subprocess.PIPE,universal_newlines=True)
while True:
out= p.stdout.read(1)
if out == '' and p.poll() != None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
- 在stdout=subprocess.PIPE中,具体发生了什么?如果我没理解错的话,stdout是指标准输出。那么我们为什么要用out = p.stdout.read(1)呢?
如果我的程序是这样的:
import sys
import subprocess
cmdping = "ping -c4 10.10.22.20"
p = subprocess.Popen(cmdping, shell=True,stdin=subprocess.PIPE,universal_newlines=True)
输出结果是:
PING 10.10.22.20 (10.10.22.20) 56(84) bytes of data.
64 bytes from 10.10.22.20: icmp_seq=1 ttl=63 time=0.951 ms
UMR-AUTO root@1-2 #64 bytes from 10.10.22.20: icmp_seq=2 ttl=63 time=0.612 ms
64 bytes from 10.10.22.20: icmp_seq=3 ttl=63 time=0.687 ms
64 bytes from 10.10.22.20: icmp_seq=4 ttl=63 time=0.638 ms
--- 10.10.22.20 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 0.612/0.722/0.951/0.134 ms
你能给我解释一下吗?我不太明白这个过程是怎么进行的,想要准确了解一下。
1 个回答
管道(pipe)是一种将多个进程连接在一起的方法,这样它们就可以通过标准流进行通信。下面是一个来自维基百科的图示,解释了不同的管道以及如何使用它们:
这意味着你可以把一个进程的输出作为另一个进程的输入,把这些进程串联起来。
你可以在三个主要的地方使用管道:标准输出(stdout)、标准输入(stdin)和标准错误(stderr)流。
这让你可以把输出重定向到其他应用程序。例如,你可能想把所有的错误输出重定向到一个程序,这个程序会把这些输出发送到数据库。
当你想在自己的程序中执行一个进程时,你需要决定如何与它进行通信。不过,大多数情况下有很多方便的方法可以使用。这些方法在底层使用管道,但提供了更友好的界面。
最简单的情况是你只想运行一个进程,关心的只是它是否成功运行。为此,你可以使用
subprocess.call
:if subprocess.call(['ping', '-c', '1', 'www.google.com']) != 0: print('There was an error')
还有一个
subprocess.check_call
。它们的功能是一样的,唯一的区别是check_call
会在出现问题时抛出CallProcessError
异常。你可以捕获这个异常并输出错误信息。如果你想运行一个命令并获取输出,可以使用
subprocess.check_output
:try: result = subprocess.check_output(['ping', '-c', '1', 'www.google.com']) print('The result is: {}'.format(result)) except subprocess.CallProcessError, e: print('There was an error: {}'.format(e))
它会返回以下内容:
>>> result = subprocess.check_output(['ping', '-c', '1', 'www.google.com']) >>> print(result) PING www.google.com (78.159.164.59) 56(84) bytes of data. 64 bytes from 78.159.164.59: icmp_seq=1 ttl=49 time=33.4 ms --- www.google.com ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 33.414/33.414/33.414/0.000 ms
运行一个进程,然后发送其他命令。为此,你可以使用
Popen.communicate
和管道。为了能够与进程进行通信,你需要创建一个管道,然后在你的程序中获取这个管道的句柄。
如果你只想发送命令,你可以创建一个指向
stdin
的管道;如果你想发送命令并读取结果,则需要创建一个指向stdin
和stdout
(可选的还有stderr
)的管道。在这个例子中,我们从Python命令行启动另一个Python命令行,然后执行
import this
并读取结果:>>> handle = subprocess.Popen(['python'], stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) >>> result, error = handle.communicate('import this') >>> print(result) The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
最后,你会注意到我传递了一个列表作为我的命令。这是推荐的方式来列出你想要启动的命令(及其选项)。
你可以使用shlex
模块来帮助你将命令字符串拆分成各个部分:
>>> import shlex
>>> cmd = 'ping -c1 google.com'
>>> parsed_cmd = shlex.split(cmd)
>>> parsed_cmd
['ping', '-c1', 'google.com']
这是一个简单的例子,但文档中展示了一个更复杂的例子。