调用系统命令之subprocess模块

除了常见的os.system和os.popen方法,官方强烈推荐使用subprocess来调用系统命令。html

这个库用起来其实很简单,按照惯例先贴一下官文关键点:python

The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.

The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle.
For more advanced use cases, the underlying Popen interface can be used directly.

推荐的使用方式是:任何场景下,只要调用run()方法便可,已经封装好了一切。
底层接口Popen也能够直接使用,不过要注意管道堵塞问题。shell

API说明以下:app

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, ...)

Run the command described by args. Wait for command to complete, then return a CompletedProcess instance. 执行命令,等待执行完成,返回CompletedProcess实例
args is required for all calls and should be a string, or a sequence of program arguments.
If passing a single string, shell must be True.
If shell is True, the specified command will be executed through the shell.
On POSIX with shell=True, the shell defaults to /bin/sh. 默认shell是/bin/sh
subprocess.PIPE
Special value that can be used as the stdin, stdout or stderr argument to Popen and indicates that a pipe to the standard stream should be opened.

subprocess.STDOUT
Special value that can be used as the stderr argument to Popen and indicates that standard error should go into the same handle as standard output.
合并错误输出到标准输出
class subprocess.CompletedProcess

The return value from run(), representing a process that has finished.

args
The arguments used to launch the process. This may be a list or a string.

returncode
Exit status of the child process. Typically, an exit status of 0 indicates that it ran successfully.
A negative value -N indicates that the child was terminated by signal N (POSIX only).

stdout
Captured stdout from the child process. A bytes sequence, or a string if run() was called with an encoding, errors, or text=True.

看不懂英文的同窗能够略过API说明,下面来讲使用姿式。ui

这里提供两种传参方式,一种是将要执行的命令以一整个字符串传入,一种是以序列方式传入,具体来讲是这样:spa

subprocess.run(["ls", "-l"])
subprocess.run('ls -l', shell=True)
r = subprocess.run('ls -l', shell=True, stdout=PIPE, stderr=subprocess.STDOUT)
print(r)
print(type(r))
print(r.stdout.decode()) # 获取结果
print(r.returncode)

仅此而已,就这么简单。指针


关于Popen,简单说几句。code

This will deadlock when using stdout=PIPE or stderr=PIPE and the child process generates enough output to a pipe,
such that it blocks waiting for the OS pipe buffer to accept more data. Use Popen.communicate() when using pipes to avoid that.

官文说Popen中使用PIPE可能会形成堵塞,建议使用Popen.communicate()来避免这个问题。
这一点,run()方法已经封装解决了,直接使用run()方法能够不理会这个问题。htm

解决这个问题还有一个思路,就是不用PIPE。使用临时文件保存输出。接口

from subprocess import Popen
from tempfile import TemporaryFile

with TemporaryFile(mode='w+b') as f:  # 使用临时文件保存输出结果,避免死锁
    with Popen('ifconfig', shell=True, stdout=f, stderr=subprocess.STDOUT) as proc:
        status = proc.wait()
        f.seek(0)  # 写文件时指针在文末因此读取文件须要移动指针
        print(f.read().decode())
        print(status)

另外,这个模块里面还有一个老方法能够用来执行命令。

implicitly invoke the system shell.

subprocess.getoutput(cmd)
Return output (stdout and stderr) of executing cmd in a shell.
r = subprocess.getoutput('ls -l')
print(r)
print(type(r))
# <class 'str'>

一句话,请使用subprocess.run()方法,其它能够忽略。

参考:
https://docs.python.org/3/library/subprocess.html
https://docs.python.org/3/library/tempfile.html

相关文章
相关标签/搜索