在Python中使用子进程的意外输出

时间:2013-06-24 21:49:26

标签: python bash subprocess

我正在尝试从我的Python(版本2.6.5)代码中运行一个shell命令,但它生成的输出不同于在shell中运行的相同命令(bash):

击:

~> ifconfig eth0 | sed -rn 's/inet addr:(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/p' | sed   's/^[ \t]*//;s/[ \t]*$//'
192.168.1.10

的Python:

>>> def get_ip():
...     cmd_string = "ifconfig eth0 | sed -rn \'s/inet addr:(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/p' | sed 's/^[ \t]*//;s/[ \t]*$//\'"
...     process = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE)
...     out, err = process.communicate()
...     return out
... 
>>> get_ip()
'\x01\n'

我的猜测是,我需要在python中运行时以某种方式转义引号,但我不知道该怎么做。

注意:我无法在需要运行此代码的计算机上安装其他模块或更新python。它需要与Python 2.6.5和标准库一样工作。

3 个答案:

答案 0 :(得分:1)

您的代码无效的原因是您没有足够的逃避。你逃脱了引号,但没有其他东西需要转义。

让我们看看你想要的命令行:

ifconfig eth0 | sed -rn 's/inet addr:(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/p' | sed   's/^[ \t]*//;s/[ \t]*$//'

打印出您的实际命令行(仅print cmd_string

ifconfig eth0 | sed -rn 's/inet addr:(([0-9]{1,3}\.){3}[0-9]{1,3}).*//p' | sed 's/^[    ]*//;s/[    ]*$//'

显然这些都不一样。关键的区别在于你的\1已被一个不可见的控制字符替换,即ord为1的那个(即ctrl-A)。 (你还用标签字符替换了每个\t,但那个可能不会破坏任何东西。)

打印出repr行(print repr(cmd_string))通常也有帮助:

"ifconfig eth0 | sed -rn 's/inet addr:(([0-9]{1,3}\\.){3}[0-9]{1,3}).*/\x01/p' | sed 's/^[ \t]*//;s/[ \t]*$//'"

\x01应该立即提醒您正在进行的操作 - 或者,即使您不理解它,它也会提醒您 某些地方是什么出错了,所以你可以在SO上做一个更简单的搜索或写一个更简单的问题。

你应该养成这样做的习惯,只要你在逃避时出错了。


然而,通常,答案很简单:不要试图找出需要转义的内容和内容,而只需使用原始字符串:

cmd_string = r"ifconfig eth0 | sed -rn 's/inet addr:(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/p' | sed   's/^[ \t]*//;s/[ \t]*$//'"

现在,当你打印出来时,你会得到:

ifconfig eth0 | sed -rn 's/inet addr:(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/p' | sed   's/^[ \t]*//;s/[ \t]*$//'

正是你想要的。

答案 1 :(得分:0)

在python中如果你在字符串周围使用双引号,那么你可以在该字符串中使用单引号而不需要转义,反之亦然。但是,反斜杠需要使用额外的\前缀进行转义。

调试时最好的选择可能是添加:

print cmd_string

在设置cmd_string之后,然后将其与原始版本进行比较,以查看是否还缺少其他字符(这些字符也需要转义)。

答案 2 :(得分:0)

你能负担得起安装sh模块吗?

>>> import re, sh
>>> get_ip = lambda: re.search(r'inet addr:(\S+)', 
                               str(sh.ifconfig('eth0'))).group(1)
>>> get_ip()
'10.0.0.202'

清洁版:

def get_ip(interface):
    ifconfig = sh.ifconfig(interface)
    match = re.search(r'inet addr:(\S+)', str(ifconfig))
    if match:
        return match.group(1)
    return None

>>> get_ip(eth0)
'192.168.1.10'

即使使用子进程,摆脱sed并使用re模块,它也会更简单,并且可以避免一些麻烦。