python的subprocess的簡單使用和注意事項

subprocess是python在2.4引入的模塊, 主要用來替代下面幾個模塊和方法:html

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

能夠參考PEP324: http://legacy.python.org/dev/peps/pep-0324/sql

這是一個用來調用外部命令的模塊, 替代了一些舊的模塊, 提供了更加友好統一的接口.shell

三個封裝方法windows

使用下面三個方法的時候, 注意兩個問題: 1. shell=True或False, 兩種解析方式是不一樣的 2. 注意PIPE的使用, 可能致使卡死緩存

subprocess.call 運行命令, 等待完成, 並返回returncode安全

subprocess.check_call 運行命令, 等待完成, 若是返回值爲0, 則返回returncode, 不然拋出帶有returncode的CalledPorcessError異常.oop

subprocess.check_output 和check_call相似, 會檢查返回值是否爲0, 返回stdout.性能

卡死常見的緣由spa

這個模塊在使用的時候, 可能會出現子進程卡死或hang住的狀況. 通常出現這種狀況的是這樣的用法.

import subprocess
import shlex

proc = subprocess.Popen(shlex.split(cmd), stdin=subprocess.PIPE,
                        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                        shell=False, universal_newlines=True)
print proc.stdout.read()

這裏的直接讀取了Popen對象的stdout, 使用了subprocess.PIPE. 這種狀況致使卡死的緣由是PIPE管道的緩存被充滿了, 沒法繼續寫入, 也沒有將緩存中的東西讀出來.

官方文檔的提示(Popen.wait()方法的Warning)

This will deadlock when using stdout=PIPE and/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 communicate() to avoid that.

爲了解決這個問題, Popen有一個communicate方法, 這個方法會將stdout和stderr數據讀入到內存, 而且會一直獨到兩個流的結尾(EOF). communicate能夠傳入一個input參數, 用來表示向stdin中寫入數據, 能夠作到進程間的數據通訊.

注意: 官方文檔中提示, 讀取的數據是緩存在內存中的, 因此當數據量很是大或者是無限制的時候, 不要使用communicate, 應該會致使OOM.

通常狀況下, stdout和stderr數據量不是很大的時候, 使用communicate不會致使問題, 量特別大的時候能夠考慮使用文件來替代PIPE, 例如stdout=open("stdout", "w")[參考1].

參考2中給出了另外一種解決的思路, 使用select來讀取Popen的stdout和stderr中的數據, select的這種用法windows下是不支持的, 不過能夠作到比較實時的讀取數據.

 

Popen中的shell參數含義

官方文檔中推薦shell=False, 這種方式更加安全, 咱們來看一下官方給出的例子. 

>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / #
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...

上面這種命令拼寫的方式會致使一個安全問題, 就是用戶能夠進行相似sql注入的shell注入, 刪除整個系統的文件, 這個是極其危險的.

shell=False會屏蔽shell中的不少特性, 因此能夠避免上述這種安全問題, 當須要暴露給用戶去使用的時候, 尤爲要注意上述的安全問題.

shell=True的時候就是按照shell的方式解析和運行的.

 

Popen的一些簡單調優思路

有個bufsize的參數, 這個默認是0, 就是不緩存, 1表示行緩存, 其餘正數表示緩存使用的大小, 負數-1表示是使用系統默認的緩存大小.

在運行性能遇到問題時, 多是緩存區未開啓或者過小, 致使了子進程被卡住, 效率低下, 能夠考慮配置爲-1或4096.

須要實時讀取stdout和stderr的, 能夠查閱[參考2], 使用select來讀取stdout和stderr流.

 

參考: 

  1. 當心subprocess的PIPE卡住你的python程序: http://noops.me/?p=92
  2. pipe large amount of data to stdin while using subprocess.Popen: http://stackoverflow.com/questions/5911362/pipe-large-amount-of-data-to-stdin-while-using-subprocess-popen
  3. python docs: http://docs.python.org/2/library/subprocess.html
相關文章
相關標籤/搜索