調用系統命令之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

相關文章
相關標籤/搜索