在Python中调用外部命令

我如何从Python脚本中调用一个外部命令(就像我在Unix shell或Windows命令提示符下键入的那样)?

答案


查看标准库中的子进程模块

from subprocess import call
call(["ls", "-l"])

子进程 vs 系统的优点是它更加灵活(你可以得到stdout,stderr,“真实”状态码,更好的错误处理等等)。

官方文档建议模块在替代使用os.system():

模块提供了产卵新的流程和检索其结果更加强大的工具; 使用该模块优于使用该函数[ os.system()]。

该“ 与子模块更换旧的功能中”部分文档可能有一些有益的食谱。

有关子流程模块的官方文档:


以下是调用外部程序的方法以及每种方法的优缺点的总结:

  1. os.system("some_command with args")将命令和参数传递给你的系统的shell。这很好,因为你可以用这种方式一次运行多个命令,并设置管道和输入/输出重定向。例如:
    os.system("some_command < input_file | another_command > output_file")  

    但是,虽然这很方便,但您必须手动处理诸如空格等外壳字符的转义。另一方面,这也可以让您运行仅仅是shell命令的命令,而不是实际上的外部程序。请参阅文档

  2. stream = os.popen("some_command with args")将会做同样的事情,os.system只不过它提供了一个类似文件的对象,您可以使用它来访问该进程的标准输入/输出。还有另外三种popen变体,它们都以不同的方式处理I / O。如果你把所有东西都作为一个字符串传递,那么你的命令被传递给shell; 如果你将它们作为列表传递给你,那么你不需要担心逃脱任何事情。请参阅文档
  3. 模块的Popensubprocess。这是为了取代它,os.popen但由于其如此全面而略微复杂一些,因此它的缺点是。例如,你会说:
    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()

    代替:

    print os.popen("echo Hello World").read()

    但是在一个统一的类中有所有的选项,而不是4个不同的popen函数是很好的。请参阅文档

  4. call来自subprocess模块的功能。这基本上和Popen类一样,并且采用了所有相同的参数,但它只是等到命令完成并给出返回码。例如:
    return_code = subprocess.call("echo Hello World", shell=True)  

    请参阅文档

  5. 如果您使用的是Python 3.5或更高版本,则可以使用新subprocess.run函数,这与上述类似,但更加灵活,并CompletedProcess在命令执行完成时返回对象。
  6. os模块还具有C程序中所有的fork / exec / spawn函数,但我不建议直接使用它们。

subprocess模块应该可能是你使用的。

最后请注意,对于所有通过shell以字符串形式执行的最终命令的方法,您负责转义它。如果您传递的字符串的任何部分无法完全信任,则存在严重的安全隐患。例如,如果用户正在输入字符串的某个/任何部分。如果您不确定,请仅使用这些方法和常量。为了给你一些暗示,请考虑下面的代码:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

并想象用户输入“我的妈妈没有爱我&& rm -rf /”。


我通常使用:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

您可以随意使用stdout管道中的数据执行任何操作。事实上,你可以简单地忽略这些参数(stdout=stderr=),它的行为就像os.system()


关于从调用者中分离子进程的一些提示(在后台启动子进程)。

假设你想从一个CGI脚本开始一个长任务,那就是子进程应该比CGI脚本的执行过程长。

子流程模块文档中的经典示例是:

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

这里的想法是,你不想在’call subprocess’行中等待,直到longtask.py完成。但是不清楚在这个例子中’这里有更多的代码’之后会发生什么。

我的目标平台是freebsd,但是开发是在Windows上进行的,所以我首先面对Windows上的问题。

在Windows(win xp)上,父进程将不会完成,直到longtask.py完成其工作。这不是你想要的CGI脚本。这个问题不是特定于Python,在PHP社区中,问题是相同的。

解决方案是在win API中将DETACHED_PROCESS 进程创建标志传递给底层的CreateProcess函数。如果你碰巧安装了pywin32,你可以从win32process模块​​导入标志,否则你应该自己定义它:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun在下面的注释中注意到,语义上正确的标志是CREATE_NEW_CONSOLE(0x00000010)* /

在freebsd上,我们遇到了另一个问题:当父进程完成时,它也完成了子进程。这并不是你想要的CGI脚本。一些实验表明,这个问题似乎是在共享sys.stdout。工作解决方案如下:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

我没有检查过其他平台上的代码,也不知道freebsd上的行为原因。如果有人知道,请分享您的想法。在Python中开始后台进程的搜索并没有任何亮点。


我建议使用子进程模块而不是os.system,因为它为你的shell进行转义,因此更安全:http://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])

import os
cmd = 'ls -al'
os.system(cmd)

如果你想返回命令的结果,你可以使用os.popen。不过,从版本2.6开始,这被弃用,以支持子流程模块,其他答案已经涵盖得很好。

添加评论

友情链接:蝴蝶教程