为什么这个Linux命令在Python中执行时不起作用?
我想执行这个Linux命令:
“cat cflow_image.py | mailx -s "CFLOW Copy" foo@.foo.com”。我的需求是把这个命令放在Python脚本里执行。我正在使用subprocess模块来实现这个。
这是我写的代码:
def send_mail(mailid):
# This is mail the testbed info to the user
mailid = args.mailID
print "* INFO file will be sent to your mailid *"
subprocess.call("cat info.txt | mailx -s \"DEVSETUP\" {0}").format(mailid)
下面是执行时出现的错误:
Traceback (most recent call last):
File "dev-installer.py", line 243, in <module>
send_mail(args.mailID)
File "dev-installer.py", line 204, in send_mail
subprocess.call("cat info.txt | mailx -s \"DEVSETUP\" {0}").format(mailid)
File "/sw/packages/python/2.7.4/lib/python2.7/subprocess.py", line 524, in call
return Popen(*popenargs, **kwargs).wait()
File "/sw/packages/python/2.7.4/lib/python2.7/subprocess.py", line 711, in __init__
errread, errwrite)
File "/sw/packages/python/2.7.4/lib/python2.7/subprocess.py", line 1308, in _execute_child
2 个回答
你在传递一个 shell 命令,所以 Python 会调用一个 shell 来解析并执行你的命令。但是你告诉 Python 去执行一个叫做 cat info.txt | mailx -s "DEVSETUP" foo@.foo.com
的命令,也就是说,系统里应该有一个这个名字的可执行文件,但实际上并没有这样的命令。
subprocess.call
支持所有 Popen
构造函数的关键字参数。你可以传递一个关键字参数 shell=True
,来告诉 Python 你传的是一些 shell 代码,而不是一个可执行文件的名字。
subprocess.call("cat info.txt | mailx -s \"DEVSETUP\" {0}".format(mailid),
shell=True)
不过要注意,mailid
的值会在 shell 代码中被插入,如果它包含了 shell 特殊字符,比如 bob@example.com (Bob Smith)
或 Bob Smith <bob@example.com>
,那么就会出问题。你应该确保对 mailid
中的特殊字符进行引号处理。
你发的命令还有一个问题,就是如果在读取 info.txt
时发生了错误,系统是不会检测到的。你可以通过避免无用的使用 cat
来解决这个问题:
subprocess.call("<info.txt mailx -s \"DEVSETUP\" {0}".format(mailid),
shell=True)
考虑到这个命令很简单,你根本不需要调用 shell。如果不涉及 shell,你就不需要担心引号的问题。你可以很容易地让 Python 打开 info.txt
文件进行读取。
body_file = open("info.txt")
status = subprocess.call(["mailx", "-s", "DEVSETUP", mailid], stdin=body_file)
body_file.close()
if status != 0:
… handle mailx failure …
subprocess.call
是用来执行一个可执行文件的。这里你不是在传递一个可执行文件的路径,而是传递一个命令行指令。
如果你想执行一个命令行指令,你需要明确告诉程序你想在一个命令行环境中执行这个指令,这可以通过在参数列表中加上 shell=True
来实现。需要注意的是,使用 shell=True
和用户提供的命令一起使用可能会带来安全隐患。
另外,如果你没有使用 shell=True
(比如你没有特别说明),那么你应该传递一个字符串列表作为参数,这个列表代表已经解析好的参数列表。举个例子:
subprocess.call('ls -l')
这会尝试执行一个名为 ls -l
的命令,如果你的 PATH
中没有这个名字的可执行文件,可能会失败,而:
subprocess.call(['ls', '-l'])
则会调用 ls
这个可执行文件,并传递参数 -l
。
如果你不想手动写这样的列表,可以使用 shlex.split
来解析“命令行”:
subprocess.call(shlex.split('executable arg1 arg2 --option1 --option2 "string argument"'))
这样会得到以下调用:
subprocess.call(['executable', 'arg1', 'arg2', '--option1', '--option2', 'string argument'])
注意引号是如何被正确处理的(不过没有进行命令行扩展!)