在Python中调用外部命令


=Start=

缘由:

Python中的subprocess模块允许你派生出新的进程,并通过pipe连接它们的标准输入/标准输出/标准出错,获取子进程的返回状态码。subprocess模块是被设计用来替代下面几个比较老的模块和函数的:

  • os.system
  • os.spawn*
  • os.popen*
  • popen2.*
  • commands.*

PEP 324 – 且 PEP324 明确推荐使用subprocess模块

正文:

在使用subprocess模块中的函数创建子进程的时候,要注意:

  1. 在创建子进程之后,父进程是否暂停,并等待子进程运行;
  2. 函数返回什么;
  3. 当returncode不为0时,父进程如何处理。
小结:
1.阻塞式,且不需要输出结果
p = subprocess.Popen(); p.communicate()/p.wait()
subprocess.call()

2.阻塞式,要检查子进程执行状态
subprocess.call()
subprocess.check_call()
subprocess.check_output()

3.阻塞式,要检查子进程执行状态,且要查看错误原因
subprocess.check_output()

4.阻塞式,手动检查子进程执行状态
p = subprocess.Popen(cmd, shell=True)
while p.poll() is None:
    time.sleep(1)
if p.returncode != 0:
    print('"{0}" exec failed.'.format(cmd))

5.非阻塞式
p = subprocess.Popen()


#常见用法
try:
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
except (OSError, ValueError) as e:
    print("%r failed, reason %s" % (cmd, str(e)))
    return -1
stdout_data, stderr_data = p.communicate()
if p.returncode != 0:
    print("%r failed, status code %s stdout %r stderr %r" % (cmd, p.returncode, stdout_data, stderr_data))
subprocess模块提供的常用函数

subprocess.call()

  • 父进程等待子进程完成
  • 返回退出信息(returncode,相当于exit code)

subprocess.check_call()

  • 父进程等待子进程完成
  • 返回0
  • 检查退出信息,如果returncode不为0,则抛出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查(见Python错误处理)。

subprocess.check_output()

  • 父进程等待子进程完成
  • 返回子进程向标准输出的输出结果
  • 检查退出信息,如果returncode不为0,则抛出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。
subprocess.Popen对象

实际上,上面的三个函数都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就需要使用Popen类,该类生成的对象用来代表子进程。

与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待(也就是阻塞block):

import subprocess
child = subprocess.Popen(["ping", "-c", "5", "www.baidu.com"])
print("parent process")

从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。

对比等待的情况:

import subprocess
child = subprocess.Popen(["ping", "-c", "5", "www.baidu.com"])
child.wait()
print("parent process")
Popen.poll()    #可用于检查子进程状态
    检查子进程是否已终止。设置并返回 returncode attribute.
Popen.wait()    #可用来阻塞父进程,等待子进程执行完毕
    等待子进程终止。设置并返回 returncode attribute.
Popen.pid
    子进程的进程号。如果你设置了`shell=True`选项,则返回的是派生shell的进程号
Popen.returncode
    子进程的返回值,由 poll() 和 wait() 进行设置(间接的也被 communicate() 设置),当值为 None 时表明进程还未终止。(在Unix系统中若返回`-N`则表明进程是被通过N号信号给终止掉的)
subprocess模块中的异常

https://docs.python.org/2/library/subprocess.html#exceptions

  • OSError #当尝试执行一个并不存在的文件时会抛出该错误,在应用中应该注意该异常
  • ValueError #当在调用Popen时使用了不合法的参数会抛出该错误
  • CalledProcessError #在调用 check_call() 和 check_output() 时,如果程序执行失败则会抛出该错误
参考链接:

=END=


《“在Python中调用外部命令”》 有 5 条评论

  1. Actual meaning of ‘shell=True’ in subprocess
    https://stackoverflow.com/questions/3172470/actual-meaning-of-shell-true-in-subprocess
    `
    The benefit of not calling via the shell is that you are not invoking a ‘mystery program.’ On POSIX, the environment variable SHELL controls which binary is invoked as the “shell.” On Windows, there is no bourne shell descendent, only cmd.exe.

    So invoking the shell invokes a program of the user’s choosing and is platform-dependent. Generally speaking, avoid invocations via the shell.

    Invoking via the shell does allow you to expand environment variables and file globs according to the shell’s usual mechanism. On POSIX systems, the shell expands file globs to a list of files. On Windows, a file glob (e.g., “*.*”) is not expanded by the shell, anyway (but environment variables on a command line are expanded by cmd.exe).

    If you think you want environment variable expansions and file globs, research the ILS attacks of 1992-ish on network services which performed subprogram invocations via the shell. Examples include the various sendmail backdoors involving ILS.

    In summary, use shell=False.
    `

  2. How do I execute a program or call a system command?
    https://stackoverflow.com/questions/89228/how-do-i-execute-a-program-or-call-a-system-command
    `
    Use subprocess.run:

    import subprocess
    subprocess.run([“ls”, “-l”])

    On Python 3.4 and earlier, use subprocess.call instead of .run:

    import subprocess
    subprocess.call([“ls”, “-l”])
    `
    https://docs.python.org/3/library/subprocess.html#subprocess.run

    Executing Shell Commands with Python
    https://stackabuse.com/executing-shell-commands-with-python/
    `
    The subprocess.run() function gives us immense flexibility that os.system() doesn’t when executing shell commands. This function is a simplified abstraction of the subprocess.Popen class, which provides additional functionality we can explore.

    在执行 shell 命令时, subprocess.run() 函数为我们提供了 os.system() 所没有的巨大灵活性。该函数是 subprocess.Popen 类的简化抽象,它提供了更多我们可以探索的功能。
    `

回复 a-z 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注