天天看點

Python Module_subprocess_子程序(程式調用)目錄前言軟體環境認識subprocessPopen Constructor(構造函數)subprocess函數最後

<a href="#%E7%9B%AE%E5%BD%95">目錄</a>

<a href="#%E5%89%8D%E8%A8%80">前言</a>

<a href="#%E8%BD%AF%E4%BB%B6%E7%8E%AF%E5%A2%83">軟體環境</a>

<a href="#%E8%AE%A4%E8%AF%86subprocess">認識subprocess</a>

<a href="#popen-constructor%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0">Popen Constructor構造函數</a>

<a href="#class-popen%E7%9A%84%E5%8F%82%E6%95%B0">Class Popen的參數</a>

<a href="#args">args</a>

<a href="#%E8%B0%83%E7%94%A8%E7%A8%8B%E5%BA%8F">調用程式</a>

<a href="#%E8%B0%83%E7%94%A8shell%E6%8C%87%E4%BB%A4">調用Shell指令</a>

<a href="#stdinstdoutstderr">stdinstdoutstderr</a>

<a href="#%E5%AE%9E%E6%97%B6%E8%8E%B7%E5%8F%96%E5%AD%90%E7%A8%8B%E5%BA%8F%E8%BE%93%E5%87%BA">實時擷取子程式輸出</a>

<a href="#%E4%B8%80%E6%AC%A1%E8%8E%B7%E5%8F%96%E5%AD%90%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%85%A8%E9%83%A8%E8%BE%93%E5%87%BA">一次擷取子程式的全部輸出</a>

<a href="#%E5%B0%86%E6%A0%87%E5%87%86%E9%94%99%E8%AF%AF%E5%92%8C%E6%A0%87%E5%87%86%E8%BE%93%E5%87%BA%E4%B8%80%E8%B5%B7%E8%BE%93%E5%87%BA">将标準錯誤和标準輸出一起輸出</a>

<a href="#shell">shell</a>

<a href="#bufsize">bufsize</a>

<a href="#closefds">close_fds</a>

<a href="#%E5%85%B6%E4%BB%96%E5%8F%82%E6%95%B0%E5%90%AB%E4%B9%89">其他參數含義</a>

<a href="#popen%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0">Popen成員函數</a>

<a href="#popenpoll">Popenpoll</a>

<a href="#popenwaittimeoutnone">PopenwaittimeoutNone</a>

<a href="#popencommunicateinputnonetimeoutnone">PopencommunicateinputNonetimeoutNone</a>

<a href="#popensendsignalsignal">Popensend_signalsignal</a>

<a href="#popenterminate">Popenterminate</a>

<a href="#popenkill">Popenkill</a>

<a href="#popen%E6%88%90%E5%91%98%E5%B1%9E%E6%80%A7">Popen成員屬性</a>

<a href="#popenpid">Popenpid</a>

<a href="#popenreturncode">Popenreturncode</a>

<a href="#popenstdin">Popenstdin</a>

<a href="#%E8%BE%93%E5%85%A5%E4%BA%A4%E4%BA%92">輸入互動</a>

<a href="#popenstdout">Popenstdout</a>

<a href="#%E8%BF%9E%E7%BB%AD%E7%9A%84%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E4%BA%A4%E4%BA%92">連續的輸入輸出互動</a>

<a href="#popenstderr">Popenstderr</a>

<a href="#subprocess%E5%87%BD%E6%95%B0">subprocess函數</a>

<a href="#subprocesscall">subprocesscall</a>

<a href="#subprocesscheckcall">subprocesscheck_call</a>

<a href="#subprocesscheckoutput">subprocesscheck_output</a>

<a href="#%E6%9C%80%E5%90%8E">最後</a>

subpocess用于在父程序中建立子程序,如果你希望在Python程式中調用外部程式,如:Powershell、shell、cmd、bat。subprocess将會是一個非常好的選擇。

系統

Win 10

軟體

Python 3.4.4

IPython 4.0.0

subprocess:The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:

subprocess的誕生是為了替代、整合以前幾種舊的建立子程序的方法,能夠實作以管道的形式連接配接子程序的stdin、stdout、stderr,并且子程序會傳回一個returncode給父程序。與C語言中的fock實作類似的功能。

Execute a child program in a new process. On POSIX, the class uses os.execvp() like behavior to execute the child program. On Windows, the class uses the Windows CreateProcess() function

在POSIX類型系統中會使用<code>os.execvp()</code>來執行子程式,而在windows環境中會使用<code>CreateProcess()</code>函數來執行,這篇博文主要記錄在windows環境下的使用。

subprocess擁有數個通過不同的方式來建立子程序的函數,但是subprocess隻有一個Popen類,同樣是用于建立子程序。使用Class Popen建立的對象擁有Popen的成員屬性和方法。

在Python 3.2之後的版本Popen對象添加了下面這種寫法:

on exit, standard file descriptors are closed, and the process is waited for.

下面介紹Popen類的參數含義。

args :should be a sequence of program arguments or else a single string.

args參數可以是String類型或者sequence類型,作為子程式的聲明。在Windows中調用的子程序API是CreateProcess([String]),是以可以接受諸如<code>notepad.exe test.txt</code>這樣的字元串來執行。但是在Linux的環境下需要接受List類型對象來分隔程式名和參數。如果是序列類型,序列的第一個參數一般也作為子程式的路徑,之後的元素作為傳入子程式的參數。官方建議args參數使用List類型對象。

調用一個Powershell腳本程式:

注意:在String前加入<code>r</code>是為了避免出現<code>SyntaxError: (unicode error)</code>

序列化傳入參數 shlex.split():

我們還可以使用shlex.split()函數來将我們所需要執行的指令序列化後再指派給args參數。

stdin\stdout\stderr指定了子程式的标準輸入、輸出、錯誤的檔案句柄(file handles)。他們可以是PIPE管道、DEVNULL(DEVNULL indicates that the special file os.devnull will be used.)、檔案描述符(existing file descriptor 一個正整數)或已存在的檔案對象(file object)。當他的值是None時,不會發生重定向,子程序的檔案句柄将會繼承父程序。而且stderr=subprocess.STDOUT能夠将标準錯誤的檔案句柄設成标準輸出(子程式的标準錯誤彙合到标準輸出)。将stdout/stderr指定為subprocess.PIPE,這樣在Popen被調用的時候會在父程序和子程序之間建立管道,子程序的标準輸出和錯誤輸出都重定向到管道,可以被父程序擷取。

這樣就可以實時的擷取子程序的輸出。

當你希望在子程式執行完後一次性擷取所有子程序輸出時,子程序對象可以調用communicate(),他會一直阻塞,等待子程序結束後擷取子程序傳回的輸出。

隻有當子程式執行結束的後才會傳回執行結果。

注意:communicate()會在通信一次之後即關閉了管道。如果希望程序之間頻繁的通信,并不建議這種方法。

可以嘗試下面的方法:

這樣無論是标準輸出還是标準錯誤輸出都會經過stdout管道傳回給父程序。

The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.

shell指定是否使用shell程式來執行子程序,當shell = True時,子程序将會由shell來執行,并且建議args參數使用String對象。

If args is a string, the string specifies the command to execute through the shell.

On POSIX with shell=True, the shell defaults to /bin/sh.

Linux下相當于<code>args = ["/bin/sh"," -c",args[0], args[1], ...]</code>

bufsize will be supplied as the corresponding argument to the open() function when creating the stdin/stdout/stderr pipe file objects.

0 means unbuffered (read and write are one system call and can return short)

1 means line buffered (only usable if universal_newlines=True i.e., in a text mode)

any other positive value means use a buffer of approximately that size

negative bufsize (the default) means the system default of io.DEFAULT_BUFFER_SIZE will be used.

當你将stdin/stdout/stderr重定向到PIPE或檔案對象時,bufsize能夠傳遞指定緩沖的方式給open()函數,并以此來建立檔案對象:

0 表示無緩沖;

1 表示行緩沖;

otherNumber 表示緩沖區大小,

-1 是bufsize參數的預設值表示使用系統緩沖(全緩沖)

注意:當子程序傳回的資料達到緩存的Size時,子程式會等待付程序讀取緩存資料。

If close_fds is true, all file descriptors except 0, 1 and 2 will be closed before the child process is executed. (POSIX only). The default varies by platform: Always true on POSIX. On Windows it is true when stdin/stdout/stderr are None, false otherwise. On Windows, if close_fds is true then no handles will be inherited by the child process. Note that on Windows, you cannot set close_fds to true and also redirect the standard handles by setting stdin, stdout or stderr.

在Unix中,如果close_fds = True,除了0、1、2之外的檔案描述符都會被關閉。如果在Windows中stdin/stdout/stderr = None,即繼承父程序時,close_fds = True ,相反為False。在Windows下也不會繼承其他檔案描述符。

注意:在Windows中不能夠在<code>close_fds = True</code>的前提下對stdin, stdout or stderr做重定向(redirect ),鎖定子程序的stdin/stdout/stderr。

given, startupinfo:If given, startupinfo will be a STARTUPINFO object, which is passed to the underlying CreateProcess function. creationflags, if given, can be CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP. (Windows only)

在Windows中,調用CreateProcess()函數建立子程序時,startupinfo參數會傳遞STARTUPINFO對象,來設定子程序的外觀等等屬性。

executable:指定要執行的程式名,很少使用,一般用args參數來指定需要執行的子程式。可以指定執行子程序的shell(e.g. bash、csh、zsh)。Unix下,預設是/bin/sh。Windows下,就是環境變量%COMSPEC%的值。windows下,隻有當你要執行的指令确實是shell内建指令(比如dir ,copy)時,你才需要指定shell=True

,而當你要執行一個基于指令行的批處理腳本的時候,不需要指定此項。

preexec_fn:If preexec_fn is set to a callable object, this object will be called in the child process just before the child is executed. (POSIX only)

鈎子函數,隻在Unix平台下有效,用于指定一個可執行對象(callable object),它将在子程序運作之前被調用。容易造成死鎖,慎用。

cmd:指定了子程序的工作目錄

注意:并不會把該目錄做為可執行檔案的搜尋目錄,是以不要把子程式檔案所在目錄設定為cwd 。

env:是一個字典類型,用于執行子程序的執行環節變量,而不使用預設繼承父程序的環境變量

universal_newlines:為True時,子程序的stdout和stderr被視為文本對象,不管是Unix的行結束符’/n’,還是Mac格式的行結束符’/r’,還是Windows格式的行結束符’/r/n’都将被視為 ‘/n’處理 。

pass_fds:is an optional sequence of file descriptors to keep open between the parent and child. Providing any pass_fds forces close_fds to be True. (POSIX only)

restore_signals:If restore_signals is true (the default) all signals that Python has set to SIG_IGN are restored to SIG_DFL in the child process before the exec. Currently this includes the SIGPIPE, SIGXFZ and SIGXFSZ signals. (POSIX only)

start_new_session:If start_new_session is true the setsid() system call will be made in the child process prior to the execution of the subprocess. (POSIX only)

Check if child process has terminated. Set and return returncode attribute.

用于檢查子程序是否已經結束。設定并傳回returncode屬性

Wait for child process to terminate. Set and return returncode attribute.

等待子程序結束。設定并傳回returncode屬性。使用Popen類建立的子程序時,父程序預設不會等待子程序結束,需要Call wait()函數來實作等待子程序結束後父程序繼續執行。

timeout:If the process does not terminate after timeout seconds, raise a TimeoutExpired exception. It is safe to catch this exception and retry the wait.

注意: 如果子程序輸出了大量資料到stdout或者stderr的管道,并達到了系統PIPE的緩存大小時,子程序會等待父程序讀取管道内的資料,若此時父程序正在wait()的話,将不會讀取管道内的資料,進而造成死鎖,建議使用Pepon.communicate()來避免這種情況。

與子程序進行互動。向stdin發送資料,可選參數input指定發送到子程序的資料。

Communicate()時從stdout和stderr中讀取資料,直到文本末尾的EOF(程序之間采用文本通信),等待子程序結束。傳回一個元組:(stdout_data, stderr_data),The data will be bytes or, if universal_newlines was True, strings.如果<code>universal_newlines = True</code>則傳回一個String。

注意:如果希望通過stdin向子程序發送資料,需要通過<code>stdin=PIPE</code>建立管道對象。同樣,如果希望從stdout和stderr擷取資料,必須将stdout和stderr設定為PIPE。

Note:If the process does not terminate after timeout seconds, a TimeoutExpired exception will be raised. Catching this exception and retrying communication will not lose any output.

如果timeout還沒有結束程序的話,需要捕捉觸發的<code>TimeoutExpired</code>異常,并且使用Popen.communicate()來保留子程式的輸出。

向子程序發送信号。

停止子程序。在windows平台下,該方法将調用Windows API TerminateProcess()來結束子程序。

殺死子程序,在Windows上調用了TerminateProcess() API。在Unix上相當于發送了信号SIGTERM和SIGKILL。

擷取子程序的程序ID。

擷取程序的傳回值。如果程序還沒有結束,傳回None。

If the stdin argument was PIPE, this attribute is a writeable stream object as returned by open()

If the stdout argument was PIPE, this attribute is a readable stream object as returned by open().

注意: run.py的flush和test.py中的flush,要記得清空緩沖區,否則程式得不到正确的輸入和輸出

If the stderr argument was PIPE, this attribute is a readable stream object as returned by open().

subprocess函數本質上是對subprocess.Popen的封裝,能夠簡便的建立子程序。當需要建立更加複雜的子程序時,建議使用Popen類,該類會生成子程序對象,且擁有多樣化的成員方法和屬性。

父程序等待子程序完成

傳回退出資訊(returncode,相當于Linux exit code)

傳回0

檢查退出資訊,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性,可用try…except…來檢查

call()和check_call()的差別:兩者的差別在于遇到錯誤的時候處理不一樣,call()傳回returncode ,check_call()再傳回returncode後還會抛出異常。check_call實際上會調用call函數,然後加入了異常處理的情況。兩者在本質上都會調用Popen().wait()來等待程序結束并傳回returncode

傳回子程序向标準輸出的輸出結果

檢查退出資訊,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性和output屬性,output屬性為标準輸出的輸出結果,可用try…except…來檢查。

在使用subprocess子產品的時候很容易卡死的情況出現,一些避免卡死的思路,我們之後再聊。 : -)