python中的subprocess.Popen() 執行shell命令


subprocess介紹

需要用到Python來執行shell腳本, 因此需要查看下subprocess模塊文檔。

根據官網文檔描述:subprocess模塊用於創建子進程, 這個模塊用於替換舊版本中的一些模塊, 如:os.system,

os.spawn*, os.popen*, os.popen*, popen2.*, commands.*, subprocess允許你能創建很多子進程, 創建的時候能能指定子進程和子進程的輸入、輸出、錯誤輸出管道, 執行后能獲取輸出結果和執行狀態。

 

subprocess模塊的常用方法用法介紹

subprocess.run() --> python3.5中新增的函數, 執行指定的命令, 等待命令執行完成后返回一個包含執行結果的 CompletedProcess類的實例。

subprocess.call(): --> 執行指定的命令, 返回命令執行狀態, 功能類似羽os.system(cmd)

subprocess.check_call(): --> python2.5中新增的函數, 執行指定的命令, 如果執行成功則返回狀態碼, 否則拋出異常。

【Tips】: 在python3.5之后的版本中, 官方文檔中提倡通過subprocess.run()函數替代其他函數來使用subprocess模塊的功能。在python3.5之前的版本中, 我們可以通過subprocess.call()來使用subprocess模塊的功能。subprocess.run(), subprocess.call(), subprocess.check_call()都是通過對subprocess.Popen的封裝來實現的高級函數。

 

 

###

三. 這幾個函數的定義以及參數

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, universal_newlines=False)

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
參數說明:

args: 要執行的shell命令, 默認應該是一個字符串序列, 如['ls', '-l'], 也可以是一個字符串如: 'ls -l', 但是此時需要把shell參數的值置為True。

【Tips】--> shell=True參數會讓subprocess.call接受字符串類型的變量作為命令, 並調用shell去執行這個字符串, 當shell=False時, subprocess.call只接受數組變量作為命令, 並將數組的第一個元素作為命令, 剩下的全部作為該命令的參數。官方不推薦使用shell=True。

p = subprocess.run(["pwd"])
# p1 = subprocess.run(["cd"])
# p2 = subprocess.run(["ls", "-l"])
print(p)

 ###


 ###

 

subprocess.Popen

subprocess的目的就是啟動一個新的進程並且與之通信。

subprocess模塊中只定義了一個類: Popen。可以使用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)

###

參數args

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

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], ...])

####

參數bufsize

參數bufsize一般0 無緩沖,1 行緩沖,其他正值 緩沖區大小,負值 采用默認系統緩沖(一般是全緩沖)

###

參數executable

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

###

參數stdin, stdout, stderr

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

subprocess.PIPE
  在創建Popen對象時,subprocess.PIPE可以初始化stdin, stdout或stderr參數。表示與子進程通信的標准流。

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

###

參數shell

如果參數shell設為true,程序將通過shell來執行。

####

參數env

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

###

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()  

###

import subprocess
p = subprocess.Popen("ls", shell=True, stdout=subprocess.PIPE)
(stdoutput, erroutput) = p.communicate()
print(stdoutput)
print(erroutput)

 

####

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是一個假想的有大量輸出的進程,當輸出達到4096時,死鎖就發生了。當然,如果我們用p.stdout.readline或者p.communicate去清理輸出,那么無論輸出多少,死鎖都是不會發生的。或者我們不使用管道,比如不做重定向,或者重定向到文件,也都是可以避免死鎖的。

subprocess還可以連接起來多個命令來執行。
在shell中我們知道,想要連接多個命令可以使用管道。
在subprocess中,可以使用上一個命令執行的輸出結果作為下一次執行的輸入。例子如下:

 ####
 
 
 
 
 
#####

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM