Python Module_subprocess_子進程(程序調用)
目錄
目錄
前言
軟件環境
認識subprocess
Popen Constructor構造函數
Class Popen的參數
args
調用程序
調用Shell指令
stdinstdoutstderr
實時獲取子程序輸出
一次獲取子程序的全部輸出
將標準錯誤和標準輸出一起輸出
shell
bufsize
close_fds
其他參數含義
Popen成員函數
Popenpoll
PopenwaittimeoutNone
PopencommunicateinputNonetimeoutNone
Popensend_signalsignal
Popenterminate
Popenkill
Popen成員屬性
Popenpid
Popenreturncode
Popenstdin
輸入交互
Popenstdout
連續的輸入輸出交互
Popenstderr
subprocess函數
subprocesscall
subprocesscheck_call
subprocesscheck_output
最后
前言
subpocess用于在父進程中創建子進程,如果你希望在Python程序中調用外部程序,如:Powershell、shell、cmd、bat。subprocess將會是一個非常好的選擇。
軟件環境
系統
Win 10
軟件
Python 3.4.4
IPython 4.0.0
認識subprocess
還是那句話,最高效的方法不過看官方文檔,傳送門:這里
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:
os.system os.spawn* os.popen* popen2.* commands.*
1
2
3
4
5
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類型系統中會使用os.execvp()來執行子程序,而在windows環境中會使用CreateProcess()函數來執行,這篇博文主要記錄在windows環境下的使用。
Popen Constructor(構造函數)
subprocess擁有數個通過不同的方式來創建子進程的函數,但是subprocess只有一個Popen類,同樣是用于創建子進程。使用Class Popen創建的對象擁有Popen的成員屬性和方法。
class subprocess.Popen( args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在Python 3.2之后的版本Popen對象添加了下面這種寫法:
on exit, standard file descriptors are closed, and the process is waited for.
with Popen(["ifconfig"], stdout=PIPE) as proc: log.write(proc.stdout.read())
1
2
下面介紹Popen類的參數含義。
Class Popen的參數
args
args :should be a sequence of program arguments or else a single string.
args參數可以是String類型或者sequence類型,作為子程序的聲明。在Windows中調用的子進程API是CreateProcess([String]),所以可以接受諸如notepad.exe test.txt這樣的字符串來執行。但是在Linux的環境下需要接受List類型對象來分隔程序名和參數。如果是序列類型,序列的第一個參數一般也作為子程序的路徑,之后的元素作為傳入子程序的參數。官方建議args參數使用List類型對象。
調用一個Powershell腳本程序:
args = [r"C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe","-ExecutionPolicy","Unrestricted",r"E:\Users\oe-fanguiju\Desktop\SendMail.ps1",str(bodyStr)] ps = subprocess.Popen(args,stdout=subprocess.PIPE)
1
2
注意:在String前加入r是為了避免出現SyntaxError: (unicode error)
序列化傳入參數 shlex.split():
我們還可以使用shlex.split()函數來將我們所需要執行的指令序列化后再賦值給args參數。
>>> import shlex, subprocess >>> command_line = 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) # Success!
1
2
3
4
5
6
7
stdin\stdout\stderr
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被調用的時候會在父進程和子進程之間建立管道,子進程的標準輸出和錯誤輸出都重定向到管道,可以被父進程獲取。
p = subprocess.Popen("/etc/service/tops-cmos/module/hadoop/test.sh", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) returncode = p.poll() while returncode is None: #檢查子程序是否結束 line = p.stdout.readline() #若沒有,則獲取子程序的輸出 returncode = p.poll() line = line.strip() print line print returncode
1
2
3
4
5
6
7
8
這樣就可以實時的獲取子進程的輸出。
當你希望在子程序執行完后一次性獲取所有子進程輸出時,子進程對象可以調用communicate(),他會一直阻塞,等待子進程結束后獲取子進程返回的輸出。
ps = subprocess.Popen(args,stdout=subprocess.PIPE) psAllReturn = ps.communicate() #Or: psReturn = ps.stdout.read() return psReturn
1
2
3
4
5
只有當子程序執行結束的后才會返回執行結果。
注意:communicate()會在通信一次之后即關閉了管道。如果希望進程之間頻繁的通信,并不建議這種方法。
可以嘗試下面的方法:
p= subprocess.Popen(["wc"], stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True) p.stdin.write('your command') #傳遞數據給子進程 p.stdin.flush() #清空子進程管道緩存 #......do something try: #......do something p.stdout.readline() #獲取子進程輸出 #......do something except: print('IOError') #......do something more p.stdin.write('your other command') p.stdin.flush() #......do something more
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ps = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
1
這樣無論是標準輸出還是標準錯誤輸出都會經過stdout管道返回給父進程。
shell
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.
In [19]: p = subprocess.Popen("ipconfig /all",stdout = subprocess.PIPE,shell=True) #args = "ipconfig /all" In [20]: pRet = p.stdout.read() #父進程獲得執行結果 #On Windows with shell=True, the COMSPEC environment variable specifies the default shell. #Win下相當于```args = ["cmd.exe","ipconfig","/all"]
1
2
3
4
5
6
On POSIX with shell=True, the shell defaults to /bin/sh.
Linux下相當于args = ["/bin/sh"," -c",args[0], args[1], ...]
bufsize
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時,子程序會等待付進程讀取緩存數據。
close_fds
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中不能夠在close_fds = True的前提下對stdin, stdout or stderr做重定向(redirect ),鎖定子進程的stdin/stdout/stderr。
In [24]: p = subprocess.Popen("ipconfig",shell=True,close_fds=True,stdout=subprocess.PIPE) ValueError: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr
1
2
3
其他參數含義
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
,而當你要執行一個基于命令行的批處理腳本的時候,不需要指定此項。
C:\Users\Username>echo %COMSPEC% C:\WINDOWS\system32\cmd.exe
1
2
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)
Popen成員函數
Popen.poll()
Check if child process has terminated. Set and return returncode attribute.
用于檢查子進程是否已經結束。設置并返回returncode屬性
Popen.wait(timeout=None)
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()來避免這種情況。
Popen.communicate(input=None,timeout=None)
與子進程進行交互。向stdin發送數據,可選參數input指定發送到子進程的數據。
Communicate()時從stdout和stderr中讀取數據,直到文本末尾的EOF(進程之間采用文本通信),等待子進程結束。返回一個元組:(stdout_data, stderr_data),The data will be bytes or, if universal_newlines was True, strings.如果universal_newlines = True則返回一個String。
注意:如果希望通過stdin向子進程發送數據,需要通過stdin=PIPE創建管道對象。同樣,如果希望從stdout和stderr獲取數據,必須將stdout和stderr設置為PIPE。
p=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (stdout_data, stderr_data) = p.communicate()
1
2
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還沒有結束進程的話,需要捕捉觸發的TimeoutExpired異常,并且使用Popen.communicate()來保留子程序的輸出。
proc = subprocess.Popen(...) try: outs, errs = proc.communicate(timeout=15) except TimeoutExpired: proc.kill() outs, errs = proc.communicate()
1
2
3
4
5
6
Popen.send_signal(signal)
向子進程發送信號。
Popen.terminate()
停止子進程。在windows平臺下,該方法將調用Windows API TerminateProcess()來結束子進程。
Popen.kill()
殺死子進程,在Windows上調用了TerminateProcess() API。在Unix上相當于發送了信號SIGTERM和SIGKILL。
Popen成員屬性
Popen.pid
獲取子進程的進程ID。
Popen.returncode
獲取進程的返回值。如果進程還沒有結束,返回None。
Popen.stdin
If the stdin argument was PIPE, this attribute is a writeable stream object as returned by open()
#test.py import sys line = sys.stdin.readline() print 'test',line #run.py from subprocess import * p = Popen('./test.py',stdin=PIPE,stdout=PIPE) p.stdin.write('say hi/n') print p.stdout.readline()
1
2
3
4
5
6
7
8
9
10
Popen.stdout
If the stdout argument was PIPE, this attribute is a readable stream object as returned by open().
# test.py import sys while True: line = sys.stdin.readline() if not line:break sys.stdout.write(line) sys.stdout.flush() # run.py import sys from subprocess import * proc = Popen('./test.py',stdin=PIPE,stdout=PIPE,shell=True) for line in sys.stdin: proc.stdin.write(line) #子進程的輸入 proc.stdin.flush() output = proc.stdout.readline() #子進程的輸出 sys.stdout.write(output) #父進程的打印
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
注意: run.py的flush和test.py中的flush,要記得清空緩沖區,否則程序得不到正確的輸入和輸出
Popen.stderr
If the stderr argument was PIPE, this attribute is a readable stream object as returned by open().
subprocess函數
subprocess函數本質上是對subprocess.Popen的封裝,能夠簡便的創建子進程。當需要創建更加復雜的子進程時,建議使用Popen類,該類會生成子進程對象,且擁有多樣化的成員方法和屬性。
subprocess.call()
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
1
父進程等待子進程完成
返回退出信息(returncode,相當于Linux exit code)
In [20]: subprocess.call("ipconfig") . . Out[20]: 0
1
2
3
4
subprocess.check_call()
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
1
父進程等待子進程完成
返回0
檢查退出信息,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性,可用try…except…來檢查
In [25]: subprocess.check_call("config",shell=True) CalledProcessError: Command 'config' returned non-zero exit status 1
1
2
3
call()和check_call()的區別:兩者的區別在于遇到錯誤的時候處理不一樣,call()返回returncode ,check_call()再返回returncode后還會拋出異常。check_call實際上會調用call函數,然后加入了異常處理的情況。兩者在本質上都會調用Popen().wait()來等待進程結束并返回returncode
subprocess.check_output()
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
1
父進程等待子進程完成
返回子進程向標準輸出的輸出結果
檢查退出信息,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性和output屬性,output屬性為標準輸出的輸出結果,可用try…except…來檢查。
最后
在使用subprocess模塊的時候很容易卡死的情況出現,一些避免卡死的思路,我們之后再聊。 : -)
Python 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。