python 中的subprocess

原本收集整理網絡上相關資料後整理:python

從python2.4版本開始,能夠用subprocess這個模塊來產生子進程,並鏈接到子進程的標準輸入/輸出/錯誤中去,還能夠獲得子進程的返回值。
subprocess意在替代其餘幾個老的模塊或者函數,好比:os.system os.spawn* os.popen* popen2.* commands.*

1、subprocess.Popen
subprocess模塊定義了一個類: Popen
class subprocess.Popen( args, 
      bufsize=0, 
      executable=None,
      stdin=None,
      stdout=None, 
      stderr=None, 
      preexec_fn=None, 
      close_fds=False, 
      shell=False, 
      cwd=None, 
      env=None, 
      universal_newlines=False, 
      startupinfo=None, 
      creationflags=0)shell


各參數含義以下:

args: 
args參數。能夠是一個字符串,能夠是一個包含程序參數的列表。要執行的程序通常就是這個列表的第一項,或者是字符串自己。
subprocess.Popen(["cat","test.txt"])
subprocess.Popen("cat test.txt")
這兩個之中,後者將不會工做。由於若是是一個字符串的話,必須是程序的路徑才能夠。(考慮unix的api函數exec,接受的是字符串
列表)
可是下面的能夠工做
subprocess.Popen("cat test.txt", shell=True)
這是由於它至關於
subprocess.Popen(["/bin/sh", "-c", "cat test.txt"])
在*nix下,當shell=False(默認)時,Popen使用os.execvp()來執行子程序。args通常要是一個【列表】。若是args是個字符串的
話,會被當作是可執行文件的路徑,這樣就不能傳入任何參數了。

注意:
shlex.split()能夠被用於序列化複雜的命令參數,好比:
>>> shlex.split('ls ps top grep pkill')
['ls', 'ps', 'top', 'grep', 'pkill']
>>>import shlex, subprocess
>>>command_line = raw_input()
/bin/cat -input test.txt -output "diege.txt" -cmd "echo '$MONEY'" 
>>>args = shlex.split(command_line)
>>> print args
['/bin/cat', '-input', 'test.txt', '-output', 'diege.txt', '-cmd', "echo '$MONEY'"]
>>>p=subprocess.Popen(args)
能夠看到,空格分隔的選項(如-input)和參數(如test.txt)會被分割爲列表裏獨立的項,但引號裏的或者轉義過的空格不在此列
。這也有點像大多數shell的行爲。

在*nix下,當shell=True時,若是arg是個字符串,就使用shell來解釋執行這個字符串。若是args是個列表,則第一項被視爲命令,
其他的都視爲是給shell自己的參數。也就是說,等效於:
subprocess.Popen(['/bin/sh', '-c', args[0], args[1], ...])

在Windows下,下面的卻又是能夠工做的
subprocess.Popen(["notepad.exe", "test.txt"])
subprocess.Popen("notepad.exe test.txt")
這是因爲windows下的api函數CreateProcess接受的是一個字符串。即便是列表形式的參數,也須要先合併成字符串再傳遞給api函數
subprocess.Popen("notepad.exe test.txt" shell=True)
等同於
subprocess.Popen("cmd.exe /C "+"notepad.exe test.txt" shell=True)

bufsize參數:
若是指定了bufsize參數做用就和內建函數open()同樣:0表示不緩衝,1表示行緩衝,其餘正數表示近似的緩衝區字節數,負數表
示使用系統默認值。默認是0。

executable參數:
指定要執行的程序。它不多會被用到:通常程序能夠由args 參數指定。若是shell=True ,executable 
能夠用於指定用哪一個shell來執行(好比bash、csh、zsh等)。*nix下,默認是 /bin/sh ,windows下,就是環境變量 COMSPEC 
的值。windows下,只有當你要執行的命令確實是shell內建命令(好比dir ,copy 等)時,你才須要指定shell=True 
,而當你要執行一個基於命令行的批處理腳本的時候,不須要指定此項。

stdin stdout和stderr:
stdin stdout和stderr,分別表示子程序的標準輸入、標準輸出和標準錯誤。可選的值有PIPE或者一個有效的文件描述符(實際上是個正
整數)或者一個文件對象,還有None。若是是PIPE,則表示須要建立一個新的管道,若是是None
,不會作任何重定向工做,子進程的文件描述符會繼承父進程的。另外,stderr的值還能夠是STDOUT
,表示子進程的標準錯誤也輸出到標準輸出。

preexec_fn參數:
若是把preexec_fn設置爲一個可調用的對象(好比函數),就會在子進程被執行前被調用。(僅限*nix)

close_fds參數:
若是把close_fds設置成True,*nix下會在開子進程前把除了0、一、2之外的文件描述符都先關閉。在 Windows下也不會繼承其餘文件描述符。

shell參數:
若是把shell設置成True,指定的命令會在shell裏解釋執行。

cwd參數:
若是cwd不是None,則會把cwd作爲子程序的當前目錄。注意,並不會把該目錄作爲可執行文件的搜索目錄,因此不要把程序文件所在
目錄設置爲cwd 。

env參數:
若是env不是None,則子程序的環境變量由env的值來設置,而不是默認那樣繼承父進程的環境變量。注意,即便你只在env裏定義了
某一個環境變量的值,也會阻止子程序獲得其
他的父進程的環境變量(也就是說,若是env裏只有1項,那麼子進程的環境變量就只有1個了)。例如:

>>> subprocess.Popen('env', env={'test':'123', 'testtext':'zzz'})
test=123
<subprocess.Popen object at 0x2870ad2c>
testtext=zzz

universal_newlines參數:
若是把universal_newlines 設置成True,則子進程的stdout和stderr被視爲文本對象,而且不論是*nix的行結束符('/n' 
),仍是老mac格式的行結束符('/r' ),仍是windows 格式的行結束符('/r/n' )都將被視爲 '/n' 。

startupinfo和creationflags參數:
若是指定了startupinfo和creationflags,將會被傳遞給後面的CreateProcess()函數,用於指定子程序的各類其餘屬性,好比主窗口樣式或者是
子進程的優先級等。(僅限Windows)

2、subprocess.PIPE
subprocess.PIPE
一個能夠被用於Popen的stdin 、stdout 和stderr 3個參數的特輸值,表示須要建立一個新的管道。
subprocess.STDOUT
一個能夠被用於Popen的stderr參數的輸出值,表示子程序的標準錯誤匯合到標準輸出。
實例:
>>>p=subprocess.Popen("df -h",shell=True,stdout=subprocess.PIPE)
>>>out=p.stdout.readlines()
>>>out
[b'Filesystem     Size    Used   Avail Capacity  Mounted on\n', b'/dev/ad0s1a    713M    313M    343M    48%    /\n', b'devfs          1.0K    1.0K      0B   100%    /dev\n', b'/dev/ad0s1e    514M    2.1M    471M     0%    /tmp\n', b'/dev/ad0s1f    4.3G    2.5G    1.4G    64%    /usr\n', b'/dev/ad0s1d    2.0G    121M    1.7G     6%    /var\n'
>>> for line in out:
...     print line.strip()
... 
Filesystem     Size    Used   Avail Capacity  Mounted on
/dev/ad0s1a    713M    313M    343M    48%    /
devfs          1.0K    1.0K      0B   100%    /dev
/dev/ad0s1e    514M    2.1M    471M     0%    /tmp
/dev/ad0s1f    4.3G    2.5G    1.4G    64%    /usr
/dev/ad0s1d    2.0G    121M    1.7G     6%    /var
stdout可使用read(),readline(),readlines()等方法

3、方便的函數
一、subprocess.call
subprocess.call (*popenargs , **kwargs )
執行命令,並等待命令結束,再返回子進程的返回值。參數同Popen,查看/usr/lib/python2.7/subprocess.py 
去掉文檔,實際上是這樣的:
def call(*popenargs, **kwargs):
    return Popen(*popenargs, **kwargs).wait()
>>> subprocess.call('ifconfig',shell=True)

二、subprocess.check_call
subprocess.check_call (*popenargs , **kwargs )
執行上面的call命令,並檢查返回值,若是子進程返回非0,則會拋出CalledProcessError異常,這個異常會有個returncode 
屬性,記錄子進程的返回值。
def check_call(*popenargs, **kwargs):
    retcode = call(*popenargs, **kwargs)
    if retcode:
        cmd = kwargs.get("args")
        raise CalledProcessError(retcode, cmd)
    return 0
>>> subprocess.check_call('ifconfig')  
>>> subprocess.call('noifconfig')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/subprocess.py", line 493, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/local/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/local/lib/python2.7/subprocess.py", line 1228, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
異常子進程裏拋出的異常,會在父進程中再次拋出。而且,異常會有個叫child_traceback的額外屬性,這是個包含子進程錯誤traceback
信息的字符串。遇到最多的錯誤回是 OSError,好比執行了一個並不存在的子程序就會產生OSError。另外,若是使用錯誤的參數調用Popen
,會拋出ValueError。當子程序返回非0時,check_call()還會產生CalledProcessError 異常。
安全性
不像其餘的popen函數,本函數不會調用/bin/sh來解釋命令,也就是說,命令中的每個字符都會被安全地傳遞到子進程裏。

三、check_output 
check_output()執行程序,並返回其標準輸出.
def check_output(*popenargs, **kwargs):
    process = Popen(*popenargs, stdout=PIPE, **kwargs)
    output, unused_err = process.communicate()
    retcode = process.poll()
    if retcode:
        cmd = kwargs.get("args")
        raise CalledProcessError(retcode, cmd, output=output)
    return output
p=subprocess.check_output('ifconfig')
結果是全部行/n分割的一個字符串
能夠直接print出來 
這裏開始

四、Popen對象windows


產生對象
p=subprocess.Popen("df -h",shell=True,stdout=subprocess.PIPE)
>>> dir(p)

Popen對象有如下方法:

Popen.poll()
檢查子進程是否已結束,設置並返回returncode屬性。

>>> p.poll()
0

Popen.wait()
等待子進程結束,設置並返回returncode屬性。
>>> p.wait() 
0
注意: 若是子進程輸出了大量數據到stdout或者stderr的管道,並達到了系統pipe的緩存大小的話,
子進程會等待父進程讀取管道,而父進程此時正wait着的話,將會產生傳說中的死鎖,後果是很是嚴重滴。建議使用
communicate() 來避免這種狀況的發生。

Popen.communicate(input=None)
和子進程交互:發送數據到stdin,並從stdout和stderr讀數據,直到收到EOF。等待子進程結束。可選的input若有有的話,要爲字符串類型。
此函數返回一個元組: (stdoutdata , stderrdata ) 。
注意,要給子進程的stdin發送數據,則Popen的時候,stdin要爲PIPE;同理,要能夠接收數據的話,stdout或者stderr也要爲PIPE。
p1=subprocess.Popen('cat /etc/passwd',shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE)              
>>> p2=subprocess.Popen('grep 0:0',shell=True,stdin=p1.stdout,stdout=subprocess.PIPE)
注意:讀到的數據會被緩存在內存裏,因此數據量很是大的時候要當心了。
>>> p.communicate()     
(b'Filesystem     Size    Used   Avail Capacity  Mounted on\n/dev/ad0s1a    713M    313M    343M    48%    /\ndevfs          1.0K    1.0K      0B   100%    /dev\n/dev/ad0s1e    514M    2.1M    471M     0%    /tmp\n/dev/ad0s1f    4.3G    2.5G    1.4G    64%    /usr\n/dev/ad0s1d    2.0G    121M    1.7G     6%    /var\n', None)

Popen.send_signal(signal)
給子進程發送signal信號。
注意:windows下目前只支持發送SIGTERM,等效於下面的terminate() 。

Popen.terminate()
中止子進程。Posix下是發送SIGTERM信號。windows下是調用TerminateProcess()這個API。

Popen.kill()
殺死子進程。Posix下是發送SIGKILL信號。windows下和terminate() 無異。

Popen.stdin
若是stdin 參數是PIPE,此屬性就是一個文件對象,不然爲None 。

Popen.stdout
若是stdout參數是PIPE,此屬性就是一個文件對象,不然爲None 。

Popen.stderr
若是stderr 參數是PIPE,此屬性就是一個文件對象,不然爲None 。

Popen.pid
子進程的進程號。注意,若是shell 參數爲True,這屬性指的是子shell的進程號。
>>> p.pid     
22303

Popen.returncode
子程序的返回值,由poll()或者wait()設置,間接地也由communicate()設置。
若是爲None,表示子進程還沒終止。
若是爲負數-N的話,表示子進程被N號信號終止。(僅限*nux)

用subprocess來代替其餘函數
均可以用subprocess來完成,咱們假定是用 「from subprocess import *」 來導入模塊的:

代替shell命令:
p=`ls -l`
等效於
p=Popen(['ls','-l'],stdout=PIPE).communicate()[0]

代替shell管道:
p=`dmesg | grep cpu`
等效於
p1=Popen(['dmesg'],stdout=PIPE)
p2=Popen(['grep','cpu'],stdin=p1.stdout,stdout=PIPE)
output = p2.communicate()[0]
output
cpu0: <ACPI CPU> on acpi0\nacpi_throttle0: <ACPI CPU Throttling> on cpu0\n

>>> p1=subprocess.Popen('cat /etc/passwd',shell=True,stdout=subprocess.PIPE)                
>>> p2=subprocess.Popen('grep 0:0',shell=True,stdin=p1.stdout,stdout=subprocess.PIPE)
>>> p3=subprocess.Popen("cut -d ':' -f 7",shell=True,stdin=p2.stdout,stdout=subprocess.PIPE)
>>> print p3.stdout.read()

代替os.system()
lsl = os.system('ls '+'-l')
這個是一個返回狀態
等效於
p=Popen('ls -l', shell=True)
lsl=os.waitpid(p.pid,0)[1]

注意:
一般並不須要用shell來調用程序。用subprocess能夠更方便地獲得子程序的返回值。
其實,更真實的替換是:
try:
    retcode = call(「mycmd」 + 」 myarg」, shell=True)
if retcode < 0:
    print >>sys.stderr, 「Child was terminated by signal」, -retcode
else:
    print >>sys.stderr, 「Child returned」, retcode
    except OSError, e:
    print >>sys.stderr, 「Execution failed:」, e

代替os.spawn系列
P_NOWAIT的例子
pid = os.spawnlp(os.P_NOWAIT, 「/bin/mycmd」, 「mycmd」, 「myarg」)
等效於
pid = Popen(["/bin/mycmd", "myarg"]).pid

P_WAIT的例子

retcode = os.spawnlp(os.P_WAIT, 「/bin/mycmd」, 「mycmd」, 「myarg」)
等效於
retcode = call(["/bin/mycmd", "myarg"])

返回值處理:

pipe = os.popen(「cmd」, ‘w’)
...
rc = pipe.close()
if rc != None and rc % 256:
    print 「There were some errors」
等效於
process = Popen(「cmd」, ‘w’, shell=True, stdin=PIPE)
...
process.stdin.close()
if process.wait() != 0:
    print 「There were some errors」
 api

參考:緩存

http://blog.csdn.net/dbzhang800/article/details/6879239安全

http://www.python.org/dev/peps/pep-0324/bash

相關文章
相關標籤/搜索