subprocess.Popen的输出
我最近在写一些Python代码,代码里用到了“command”。
代码运行得挺好的,但后来我在Python的文档里看到“command”已经被弃用了,并且在Python 3中会被移除,所以我应该用“subprocess”来替代。
我心想:“好吧,我可不想让我的代码变成过时的东西,所以我得赶紧改。”
问题是,subprocess.Popen
似乎会在任何输出的开头加上一些奇怪的字符串,比如:
<subprocess.Popen object at 0xb7394c8c>
我看到的所有例子里都有这个,看起来大家都默认它总是会出现。
这段代码:
#!/usr/bin/python
import subprocess
output = subprocess.Popen("ls -al", shell=True)
print output
输出的是:
<subprocess.Popen object at 0xb734b26c>
brettg@underworld:~/dev$ total 52
drwxr-xr-x 3 brettg brettg 4096 2011-05-27 12:38 .
drwxr-xr-x 21 brettg brettg 4096 2011-05-24 17:40 ..
<trunc>
这样正常吗?如果我把它用在一个更大的程序里,输出各种格式化的信息到控制台,就会搞得一团糟。
我用这个命令来获取某个接口的IP地址,使用ifconfig配合一些grep和awk来提取地址。
考虑这段代码:
#!/usr/bin/python
import commands,subprocess
def new_get_ip (netif):
address = subprocess.Popen("/sbin/ifconfig " + netif + " | grep inet | grep -v inet6 | awk '{print $2}' | sed 's/addr://'i", shell=True)
return address
def old_get_ip (netif):
address = commands.getoutput("/sbin/ifconfig " + netif + " | grep inet | grep -v inet6 | awk '{print $2}' | sed 's/addr://'i")
return address
print "OLD IP is :",old_get_ip("eth0")
print ""
print "NEW IP is :",new_get_ip("eth0")
它返回的是:
brettg@underworld:~/dev$ ./IPAddress.py
OLD IP is : 10.48.16.60
NEW IP is : <subprocess.Popen object at 0xb744270c>
brettg@underworld:~/dev$ 10.48.16.60
说实话,这样看起来真不怎么样。
显然,我在这里有些搞不清楚。我刚学Python,所以我肯定是哪里做错了,但我在谷歌上搜索了很多次也没找到解决办法。
如果我想要更干净的输出,我是不是得手动去掉那些多余的内容,还是说我调用subprocess.Popen
的方式不对?
3 个回答
变量 output
里面并不是一个字符串,它其实是一个用来装 subprocess.Popen()
函数的东西。你不需要去打印它。下面这段代码,
import subprocess
output = subprocess.Popen("ls -al", shell=True)
运行得很好,但不会打印出那种难看的内容: <subprocess.Popen object at 0xb734b26c>
。
查看 subprocess模块的文档,可以获取更多信息。
下面是如何使用subprocess模块获取程序的标准输出和错误输出:
from subprocess import Popen, PIPE, STDOUT
cmd = 'echo Hello World'
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
output = p.stdout.read()
print output
结果:
b'Hello\r\n'
你可以用PowerShell运行命令并查看结果:
from subprocess import Popen, PIPE, STDOUT
cmd = 'powershell.exe ls'
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
output = p.stdout.read()
这个“丑陋的字符串”就是它应该打印的内容。Python 正在正确地输出 repr(subprocess.Popen(...))
,就像你输入 print(open('myfile.txt'))
时会看到的结果一样。
而且,Python 并不知道什么内容被输出到标准输出(stdout)。你看到的输出并不是来自 Python,而是来自进程的标准输出和标准错误(stderr),这些内容被直接发送到你的终端,就像垃圾信息一样,根本没有经过 Python 处理。这就像你运行了一个程序 someprogram &
,但没有把它的标准输出和标准错误重定向到 /dev/null
,然后你再运行其他命令时,偶尔会看到这个程序的垃圾信息。为了重复和澄清:
<subprocess.Popen object at 0xb734b26c> <-- output of python program
brettg@underworld:~/dev$ total 52 <-- spam from your shell, not from python
drwxr-xr-x 3 brettg brettg 4096 2011-05-27 12:38 . <-- spam from your shell, not from python
drwxr-xr-x 21 brettg brettg 4096 2011-05-24 17:40 .. <-- spam from your shell, not from python
...
为了捕获标准输出,你必须使用 .communicate()
函数,像这样:
#!/usr/bin/python
import subprocess
output = subprocess.Popen(["ls", "-a", "-l"], stdout=subprocess.PIPE).communicate()[0]
print output
另外,你永远不想使用 shell=True
,因为这会带来安全隐患(对于未经处理的输入来说是一个大安全隐患,即使没有输入也会有小隐患,因为它允许通过修改 shell 环境进行本地攻击)。出于安全原因,以及为了避免错误,通常你应该传递一个列表而不是字符串。如果你懒惰,可以用 "ls -al".split(),虽然这样不太好,但如果你像这样做 ("ls -l %s"%unsanitizedInput).split(),那就会有安全隐患。