python subprocess執行shell命令

subprocess的目的就是啓動一個新的進程而且與之通訊。shell

subprocess模塊中只定義了一個類: Popen。可使用Popen來建立進程,並與進程進行復雜的交互。它的構造函數以下:windows

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)

參數args能夠是字符串或者序列類型(如:list,元組),用於指定進程的可執行文件及其參數。若是是序列類型,第一個元素一般是可執行文件的路徑。咱們也能夠顯式的使用executeable參數來指定可執行文件的路徑。api

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()能夠被用於序列化複雜的命令參數,好比:unix

>>> 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的行爲。code

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

參數bufsize通常0 無緩衝,1 行緩衝,其餘正值 緩衝區大小,負值 採用默認系統緩衝(通常是全緩衝)繼承

executable通常不用,args字符串或列表第一項表示程序名進程

參數stdin, stdout, stderr分別表示程序的標準輸入、輸出、錯誤句柄。他們能夠是PIPE,文件描述符或文件對象,也能夠設置爲None,表示從父進程繼承。字符串

若是參數shell設爲true,程序將經過shell來執行。

參數env是字典類型,用於指定子進程的環境變量。若是env = None,子進程的環境變量將從父進程中繼承。

subprocess.PIPE
  在建立Popen對象時,subprocess.PIPE能夠初始化stdin, stdout或stderr參數。表示與子進程通訊的標準流。

subprocess.STDOUT
  建立Popen對象時,用於初始化stderr參數,表示將錯誤經過標準輸出流輸出。

Popen的方法:

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

Popen.wait()
  等待子進程結束。設置並返回returncode屬性。

Popen.communicate(input=None)
  與子進程進行交互。向stdin發送數據,或從stdout和stderr中讀取數據。可選參數input指定發送到子進程的參數。Communicate()返回一個元組:(stdoutdata, stderrdata)。注意:若是但願經過進程的stdin向其發送數據,在建立Popen對象的時候,參數stdin必須被設置爲PIPE。一樣,若是但願從stdout和stderr獲取數據,必須將stdout和stderr設置爲PIPE。

Popen.send_signal(signal)
  向子進程發送信號。

Popen.terminate()
  中止(stop)子進程。在windows平臺下,該方法將調用Windows API TerminateProcess()來結束子進程。

Popen.kill()
  殺死子進程。

Popen.pid
  獲取子進程的進程ID。

Popen.returncode
  獲取進程的返回值。若是進程尚未結束,返回None。

進程通訊:

若是想獲得進程的輸出,管道是個很方便的方法:

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)  
(stdoutput,erroutput) = p.communicate()

p.communicate會一直等到進程退出,並將標準輸出和標準錯誤輸出返回,這樣就能夠獲得子進程的輸出了。

上面的例子經過communicate給stdin發送數據,而後使用一個tuple接收命令的執行結果。

上面,標準輸出和標準錯誤輸出是分開的,也能夠合併起來,只須要將stderr參數設置爲subprocess.STDOUT就能夠了,這樣子:

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
(stdoutput,erroutput) = p.communicate()

若是你想一行行處理子進程的輸出,也沒有問題:

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
while True:  
    buff = p.stdout.readline()  
    if buff == '' and p.poll() != None:  
        break

死鎖

可是若是你使用了管道,而又不去處理管道的輸出,那麼當心點,若是子進程輸出數據過多,死鎖就會發生了,好比下面的用法:

p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
p.wait()

longprint是一個假想的有大量輸出的進程,那麼在個人xp, Python2.5的環境下,當輸出達到4096時,死鎖就發生了。固然,若是咱們用p.stdout.readline或者p.communicate去清理輸出,那麼不管輸出多少,死鎖都是不會發生的。或者咱們不使用管道,好比不作重定向,或者重定向到文件,也都是能夠避免死鎖的。

subprocess還能夠鏈接起來多個命令來執行。
在shell中咱們知道,想要鏈接多個命令可使用管道。
在subprocess中,可使用上一個命令執行的輸出結果做爲下一次執行的輸入。例子以下:

相關文章
相關標籤/搜索