1. 程式人生 > >Python 中的 Subprocess 多程序

Python 中的 Subprocess 多程序

摘自:http://li2z.cn/2010/04/14/python_subprocess/

此文和python內建函式一樣,內容全部出自python官方文件,但是會有自己的理解,並非單純的翻譯。所以,如果我理解有誤,歡迎指正,謝謝。

從python2.4版本開始,你就可以用可以用subprocess這個模組來產生子程序,並連線到子程序的標準輸入/輸出/錯誤中去,還可以得到子程序的返回值。subprocess意在替代其他幾個老的模組或者函式,比如:

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

下面將一一介紹如何用subprocess來替代這些函式或者模組。

使用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)

各引數含義如下:

args需要是一個字串,或者包含程式引數的列表。要執行的程式一般就是這個列表的第一項,或者是字串本身。但是也可以用executable引數來明確指出。當executable

引數不為空時,args裡的第一項仍被認為是程式的“命令名”,不同於真正的可執行檔案的檔名,這個“命令名”是一個用來顯示的名稱,例如執行*nix下的 ps 命令,顯示出來的就是這個“命令名”。

在*nix下,當shell=False(預設)時,Popen使用os.execvp()來執行子程式。args一般要是一個列表。如果args是個字串的話,會被當做是可執行檔案的路徑,這樣就不能傳入任何引數了。

注意:
shlex.split()可以被用於序列化複雜的命令引數,比如:

>>> import shlex, subprocess
>>> command_line = raw_input
() /bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'" >>> args = shlex.split(command_line) >>> print args ['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"] >>> p = subprocess.Popen(args) # 成功執行!

可以看到,空格分隔的選項(如-input)和引數(如eggs.txt)會被分割為列表裡獨立的項,但引號裡的或者轉義過的空格不在此列。這也有點像大多數shell的行為。

在*nix下,當shell=True時,如果args是個字串,就使用shell來解釋執行這個字串。如果args是個列表,則第一項被視為命令,其餘的都視為是給shell本身的引數。也就是說,等效於:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

在windows下,Popen使用接受字串引數的CreateProcess()來執行子程式。如果args是個列表,它會被先用list2cmdline()轉換成字串。

如果指定了bufsize引數,作用就和內建函式open()一樣:0表示不緩衝,1表示行緩衝,其他正數表示近似的緩衝區位元組數,負數表示使用系統預設值。預設是0。

executable引數指定要執行的程式。它很少會被用到:一般程式可以由args引數指定。如果shell=Trueexecutable可以用於指定用哪個shell來執行(比如bash、csh、zsh等)。*nix下,預設是 /bin/sh,windows下,就是環境變數 COMSPEC的值。windows下,只有當你要執行的命令確實是shell內建命令(比如dircopy等)時,你才需要指定shell=True,而當你要執行一個基於命令列的批處理指令碼的時候,不需要指定此項。

stdinstdoutstderr分別表示子程式的標準輸入、標準輸出和標準錯誤。可選的值有PIPE(見下面的描述)或者一個有效的檔案描述符(其實是個正整數)或者一個檔案物件,還有None。如果是PIPE,則表示需要建立一個新的管道,如果是None,不會做任何重定向工作,子程序的檔案描述符會繼承父程序的。另外,stderr的值還可以是STDOUT(見下),表示子程序的標準錯誤也輸出到標準輸出。

如果把preexec_fn設定為一個可呼叫的物件(比如函式),就會在子程序被執行前被呼叫。(僅限*nix)

如果把close_fds設定成True,*nix下會在開子程序前把除了0、1、2以外的檔案描述符都先關閉。在Windows下也不會繼承其他檔案描述符。

如果把shell設定成True,指定的命令會在shell裡解釋執行,這個前面已經說得比較詳細了。

如果cwd不是None,則會把cwd做為子程式的當前目錄。注意,並不會把該目錄做為可執行檔案的搜尋目錄,所以不要把程式檔案所在目錄設定為cwd

如果env不是None,則子程式的環境變數由env的值來設定,而不是預設那樣繼承父程序的環境變數。注意,即使你只在env裡定義了某一個環境變數的值,也會阻止子程式得到其他的父程序的環境變數(也就是說,如果env裡只有1項,那麼子程序的環境變數就只有1個了)。例如:

>>> subprocess.Popen('env', env={'xxx':'123', 'yyy':'zzz'})
<subprocess.Popen object at 0xb694112c>
>>> xxx=123
yyy=zzz

如果把universal_newlines設定成True,則子程序的stdout和stderr被視為文字物件,並且不管是*nix的行結束符('\n'),還是老mac格式的行結束符('\r'),還是windows格式的行結束符('\r\n')都將被視為 '\n'

如果指定了startupinfocreationflags,將會被傳遞給後面的CreateProcess()函式,用於指定子程式的各種其他屬性,比如主視窗樣式或者是子程序的優先順序等。(僅限Windows)

介紹完Popen的各引數,再來看下兩個小東西:

subprocess.PIPE
一個可以被用於Popen的stdinstdoutstderr3個引數的特輸值,表示需要建立一個新的管道。

subprocess.STDOUT
一個可以被用於Popen的stderr引數的特輸值,表示子程式的標準錯誤匯合到標準輸出。

方便的函式

subprocess.call(*popenargs,**kwargs)
執行命令,並等待命令結束,再返回子程序的返回值。引數同Popen,因為開啟/usr/lib/python2.6/subprocess.py 你就知道,去掉文件,其實是這樣的:

def call(*popenargs, **kwargs):
    return Popen(*popenargs, **kwargs).wait()

subprocess.check_call(*popenargs,**kwargs)
執行上面的call命令,並檢查返回值,如果子程序返回非0,則會丟擲CalledProcessError異常,這個異常會有個returncode屬性,記錄子程序的返回值。

>>> subprocess.check_call('false')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/subprocess.py", line 498, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'false' returned non-zero exit status 1

異常

子程序裡丟擲的異常,會在父程序中再次丟擲。並且,異常會有個叫child_traceback的額外屬性,這是個包含子程序錯誤traceback資訊的字串。

遇到最多的錯誤回是 OSError,比如執行了一個並不存在的子程式就會產生OSError。

另外,如果使用錯誤的引數呼叫Popen,會丟擲ValueError。

當子程式返回非0時,check_call()還會產生CalledProcessError異常。

安全性

不像其他的popen函式,本模組不會偷偷地呼叫/bin/sh來解釋命令,也就是說,命令中的每一個字元都會被安全地傳遞到子程序裡。

Popen物件

Popen物件有以下方法:

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

Popen.wait()
等待子程序結束,設定並返回 returncode 屬性。

注意:如果子程序輸出了大量資料到stdout或者stderr的管道,並達到了系統pipe的快取大小的話,子程序會等待父程序讀取管道,而父程序此時正wait著的話,將會產生傳說中的死鎖,後果是非常嚴重滴。建議使用communicate()來避免這種情況的發生。

Popen.communicate(input=None)
和子程序互動:傳送資料到stdin,並從stdout和stderr讀資料,直到收到EOF。等待子程序結束。可選的input如有有的話,要為字串型別。
此函式返回一個元組: (stdoutdata, stderrdata)。
注意,要給子程序的stdin傳送資料,則Popen的時候,stdin要為PIPE;同理,要可以收資料的話,stdout或者stderr也要為PIPE。

注意:讀到的資料會被快取在記憶體裡,所以資料量非常大的時候要小心了。

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的程序號。

Popen.returncode
子程式的返回值,由poll()或者wait()設定,間接地也由communicate()設定。
如果為None,表示子程序還沒終止。
如果為負數-N的話,表示子程序被N號訊號終止。(僅限*nux)

用subprocess來代替其他函式

在這節裡,舉一些常用的例子,都可以用subprocess來完成,我們假定是用 “from subprocess import*” 來匯入模組的:

代替shell命令:

output=`mycmd myarg`
等效於
output = Popen(["mycmd", "myarg"],stdout=PIPE).communicate()[0]

代替shell管道:

output=`dmesg | grep hda`
等效於
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

代替os.system()

sts = os.system(“mycmd” + ” myarg”)
等效於
p = Popen(“mycmd” + ” myarg”, shell=True)
sts = os.waitpid(p.pid, 0)[1]

注意:

  • 通常並不需要用shell來呼叫程式。
  • 用subprocess可以更方便地得到子程式的返回值。

其實,更真實的替換是:

try:
retcode = call(“mycmd” + ” myarg”, shell=True)
if retcode < 0:
print >>sys.stderr, “Child wasterminated by signal”, -retcode
else:
print >>sys.stderr, “Child returned”,retcode
except OSError, e:
print >>sys.stderr, “Executionfailed:”, 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"])

Vector的例子

os.spawnvp(os.P_NOWAIT, path, args)
等效於
Popen([path] + args[1:])

關於環境變數的例子

os.spawnlpe(os.P_NOWAIT, “/bin/mycmd”, “mycmd”, “myarg”,env)
等效於
Popen(["/bin/mycmd", "myarg"], env={“PATH”: “/usr/bin”})

代替os.popen(),os.popen2(),os.popen3()

pipe = os.popen(“cmd”, ‘r’, bufsize)
等效於
pipe = Popen(“cmd”, shell=True, bufsize=bufsize,stdout=PIPE).stdout

pipe = os.popen(“cmd”, ‘w’, bufsize)
等效於
pipe = Popen(“cmd”, shell=True, bufsize=bufsize,stdin=PIPE).stdin

(child_stdin, child_stdout) = os.popen2(“cmd”, mode,bufsize)
等效於
p = Popen(“cmd”, shell=True, bufsize=bufsize, stdin=PIPE,stdout=PIPE, close_fds=True)
(child_stdin, child_stdout) = (p.stdin, p.stdout)

(child_stdin, child_stdout, child_stderr) = os.popen3(“cmd”,mode, bufsize)
等效於
p = Popen(“cmd”, shell=True, bufsize=bufsize, stdin=PIPE,stdout=PIPE, stderr=PIPE, close_fds=True)
(child_stdin, child_stdout, child_stderr) = (p.stdin, p.stdout,p.stderr)

(child_stdin, child_stdout_and_stderr) = os.popen4(“cmd”, mode,bufsize)
等效於
p = Popen(“cmd”, shell=True, bufsize=bufsize, stdin=PIPE,stdout=PIPE, stderr=STDOUT, close_fds=True)
(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)

*nix下,os.popen2, os.popen3, os.popen4也可以接受一個列表做為執行的命令,這時引數會被直接傳給程式,而不經過shell的解釋轉換。如下:

(child_stdin, child_stdout) = os.popen2(["/bin/ls", "-l"], mode,bufsize)
等效於
p = Popen(["/bin/ls", "-l"], bufsize=bufsize, stdin=PIPE,stdout=PIPE)
(child_stdin, child_stdout) = (p.stdin, p.stdout)

返回值處理:

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”

代替popen2模組裡的函式:

(child_stdout, child_stdin) = popen2.popen2(“somestring”,bufsize, mode)
等效於
p = Popen(["somestring"], shell=True, bufsize=bufsize, stdin=PIPE,stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

*nix下,popen2也可以接受一個列表做為執行的命令,這時引數會被直接傳給程式,而不經過shell的解釋轉換。如下:

(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"],bufsize, mode)
等效於
p = Popen(["mycmd", "myarg"], bufsize=bufsize, stdin=PIPE,stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

  • 執行失敗的時候Popen會丟擲異常
  • capturestderr引數用stderr代替
  • stdin=PIPEstdout=PIPE 必須要指定
  • popen2預設會關掉所有檔案描述符,而Popen要指定close_fds=True

相關推薦

Python程序小示例

#!/usr/bin/python # -*- coding:utf-8 -*- import requests import json import time from multiprocessing import Pool def func(name): print('

Python之——Python程序執行緒

轉載請註明出處:https://blog.csdn.net/l1028386804/article/details/83042246 一、多程序 Python實現對程序的方式主要有兩種,一種方法是使用os模組中的fork方法,另一種方法是使用multiprocessing模組。區別在於:

python程序,執行緒,死鎖,協程

本人根據自己的理解來總結的,如果有錯誤的地方還請各位大佬指正,謝謝了. 程序:程式是計算機可執行的二進位制資料,只有被作業系統呼叫的時候才開始它們的生命週期.程序就是程式的一次執行,擁有自己的地址空間,記憶體,程序id(pid),資料棧及其他記錄其執行軌跡的輔助資料;最小的

python程序執行緒

作者:liuyazhuang  來源:CSDN  原文:https://blog.csdn.net/l1028386804/article/details/83042246?utm_source=copy  轉載出處:https://blog.csdn.net/l102838

Python使用程序複製檔案

使用fork()函式建立父子程序,父程序複製檔案的前半部分,子程序複製檔案的後半部分 #!/usr/bin/python #coding=utf-8 import sys,os #將第一引數作為原始

Python Subprocess 程序

摘自:http://li2z.cn/2010/04/14/python_subprocess/此文和python內建函式一樣,內容全部出自python官方文件,但是會有自己的理解,並非單純的翻譯。所以,如果我理解有誤,歡迎指正,謝謝。 從python2.4版本開始,你就可以

python執行緒threading之儲存程序結果Queue

程式碼實現功能,將資料列表中的資料傳入,使用四個執行緒處理,將結果儲存在Queue中,執行緒執行完後,從Queue中獲取儲存的結果 import threading from queue import Queue def job(l, q): for i in range

理解一下Python執行緒,程序,協程

程序 一個執行的程式(程式碼)就是一個程序,沒有執行的程式碼叫程式,程序是系統資源分配的最小單位,程序擁有自己獨立的記憶體空間,所以程序間資料不共享,開銷大。 執行緒, 排程執行的最小單位,也叫執行路徑,不能獨立存在,依賴程序存在一個程序至少有一個執行緒,叫主執行緒,而多

python爬蟲入門八:多程序/執行緒 python佇列Queue Python多執行緒(2)——執行緒同步機制 python學習筆記——多程序中共享記憶體Value & Array python多程序 Python多程序 Python 使用multiprocessing 特別耗記

什麼是多執行緒/多程序 引用蟲師的解釋: 計算機程式只不過是磁碟中可執行的,二進位制(或其它型別)的資料。它們只有在被讀取到記憶體中,被作業系統呼叫的時候才開始它們的生命期。 程序(有時被稱為重量級程序)是程式的一次執行。每個程序都有自己的地址空間,記憶體,資料棧以及其它記錄其執行軌跡的輔助資料

Python線程

info print pre lock map __main__ color 開啟 self 1、什麽是線程   進程其實不是一個執行單位,進程是一個資源單位   每個進程內自帶一個線程,線程才是CPU上的執行單位    如果把操作系統比喻為一座工廠   在工廠內每造出一個

三十六、python subprocess介紹

空格 環境變量 不能 startup false 字符 nes import all import subprocess1.執行系統命令subprocess.call(‘ipconfig‘) #shell=False時,拼接命令分開寫,放在列表中,等於True時,可寫一塊,

黑客程式設計,基於Python+協程+程序的弱密碼掃描器!

  聽說不想扯淡的程式猿,不是一隻好猿。所以今天來扯扯淡,不貼程式碼,只講設計思想。 0×00 起 – 初始設計 我們的目標是設計一枚通用的弱密碼掃描器,基本功能是針對不同型別的弱密碼,可方便的擴充套件,比如新增SSH、SVN、phpmyadmin的弱密碼掃描功能。我們設

Python multiprocessing (程序)使用

官方文件 https://docs.python.org/3.6/library/multiprocessing.html from multiprocessing import Pool, Manager import time, random, os # 需要執行的函式 def f

python執行緒threading之新增執行緒:Thread()

百度百科:多執行緒 多執行緒(英語:multithreading),是指從軟體或者硬體上實現多個執行緒併發執行的技術。具有多執行緒能力的計算機因有硬體支援而能夠在同一時間執行多於一個執行緒,進而提升整體處理效能。具有這種能力的系統包括對稱多處理機、多核心處理器以及晶片級多處理(Chi

Python執行緒程式設計,執行緒安全與鎖(一) 聊聊Python的GIL 聊聊Python的GIL python基礎之執行緒鎖機制 python--threading執行緒總結 Python3入門之執行緒threading常用方法

1. 多執行緒程式設計與執行緒安全相關重要概念 在我的上篇博文 聊聊Python中的GIL 中,我們熟悉了幾個特別重要的概念:GIL,執行緒,程序, 執行緒安全,原子操作。 以下是簡單回顧,詳細介紹請直接看聊聊Python中的GIL  GIL:&n

python

  多工 多工 什麼是任務 一個電腦執行這的軟體 什麼是多工 電腦同時執行著的多個軟體 多工原理 時間片的輪轉 並行與併發 併發:假的多工,多個任務共用一個核

pythonsocket、程序、執行緒、協程、池的建立方式和應用場景

程序 場景 利用多核、高計算型的程式、啟動數量有限 程序是計算機中最小的資源分配單位 程序和執行緒是包含關係 每個程序中都至少有一條執行緒 可以利用多核,資料隔離

python學習筆記--7.python執行緒

這是在學習Python的時候做的筆記,有些時間了,大概是按照一本挺實用的入門書籍學的,我學習程式設計的思路一般是掌握基礎的變數型別,語法-分支結構 函式呼叫 類建立 結構體定義,記錄一些簡單的例項,剩下的就是需要用什麼百度現學。 對我來說python的優勢是,

Python Pool類程序 apply_async 非同步變成了阻塞解決方法

出錯程式碼: if __name__ == '__main__': ip_arr=get_ip_list() pool=Pool(processes=4) for temp in ip_arr: res = pool.apply_as

Python執行緒程式設計,執行緒安全與鎖(二) Python執行緒程式設計,執行緒安全與鎖(一)

在我的上篇博文Python中的多執行緒程式設計,執行緒安全與鎖(一)中,我們熟悉了多執行緒程式設計與執行緒安全相關重要概念, Threading.Lock實現互斥鎖的簡單示例,兩種死鎖(迭代死鎖和互相等待死鎖)情況及處理。今天我們將聚焦於Python的Threading模組總結和執行緒同步問題。