subprocess.Popen的输出

8 投票
3 回答
22590 浏览
提问于 2025-04-16 18:26

我最近在写一些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 个回答

-1

变量 output 里面并不是一个字符串,它其实是一个用来装 subprocess.Popen() 函数的东西。你不需要去打印它。下面这段代码,

import subprocess
output = subprocess.Popen("ls -al", shell=True)

运行得很好,但不会打印出那种难看的内容: <subprocess.Popen object at 0xb734b26c>

1

查看 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()

有用的链接

10

这个“丑陋的字符串”就是它应该打印的内容。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(),那就会有安全隐患。

撰写回答