今天小編就為大家分享一篇python中的subprocess.Popen()使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
從python2.4版本開始,可以用subprocess這個模塊來產生子進程,並連接到子進程的標准輸入/輸出/錯誤中去,還可以得到子進程的返回值。
subprocess意在替代其他幾個老的模塊或者函數,比如:os.system os.spawn* os.popen* popen2.* commands.*
一、subprocess.Popen
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:
args參數。可以是一個字符串,可以是一個包含程序參數的列表。要執行的程序一般就是這個列表的第一項,或者是字符串本身。
- 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的行為。
在linux下,當shell=True時,如果arg是個字符串,就使用shell來解釋執行這個字符串。如果args是個列表,則第一項被視為命令,其余的都視為是給shell本身的參數。也就是說,等效於:
- subprocess.Popen(['/bin/sh', '-c', args[0], args[1], ...])
在Windows下,下面的卻又是可以工作的
- subprocess.Popen(["notepad.exe", "test.txt"])
- subprocess.Popen("notepad.exe test.txt")
這是由於windows下的api函數CreateProcess接受的是一個字符串。即使是列表形式的參數,也需要先合並成字符串再傳遞給api函數
- subprocess.Popen("notepad.exe test.txt" shell=True)
等同於
- subprocess.Popen("cmd.exe /C "+"notepad.exe test.txt" shell=True)
bufsize參數:
如果指定了bufsize參數作用就和內建函數open()一樣:0表示不緩沖,1表示行緩沖,其他正數表示近似的緩沖區字節數,負數表示使用系統默認值。默認是0。
executable參數:
指定要執行的程序。它很少會被用到:一般程序可以由args 參數指定。如果shell=True ,executable 可以用於指定用哪個shell來執行(比如bash、csh、zsh等)。*nix下,默認是 /bin/sh ,windows下,就是環境變量 COMSPEC 的值。windows下,只有當你要執行的命令確實是shell內建命令(比如dir ,copy 等)時,你才需要指定shell=True ,而當你要執行一個基於命令行的批處理腳本的時候,不需要指定此項。
stdin stdout和stderr:
stdin stdout和stderr,分別表示子程序的標准輸入、標准輸出和標准錯誤。可選的值有PIPE或者一個有效的文件描述符(其實是個正整數)或者一個文件對象,還有None。如果是PIPE,則表示需要創建一個新的管道,如果是None,不會做任何重定向工作,子進程的文件描述符會繼承父進程的。另外,stderr的值還可以是STDOUT,表示子進程的標准錯誤也輸出到標准輸出。
preexec_fn參數:
如果把preexec_fn設置為一個可調用的對象(比如函數),就會在子進程被執行前被調用。(僅限*nix)
close_fds參數:
如果把close_fds設置成True,*nix下會在開子進程前把除了0、1、2以外的文件描述符都先關閉。在 Windows下也不會繼承其他文件描述符。
shell參數:
如果把shell設置成True,指定的命令會在shell里解釋執行。
cwd參數:
如果cwd不是None,則會把cwd做為子程序的當前目錄。注意,並不會把該目錄做為可執行文件的搜索目錄,所以不要把程序文件所在目錄設置為cwd 。
env參數:
如果env不是None,則子程序的環境變量由env的值來設置,而不是默認那樣繼承父進程的環境變量。注意,即使你只在env里定義了某一個環境變量的值,也會阻止子程序得到其他的父進程的環境變量(也就是說,如果env里只有1項,那么子進程的環境變量就只有1個了)。例如:
- >>> subprocess.Popen('env', env={'test':'123', 'testtext':'zzz'})
- test=123
- <subprocess.Popen object at 0x2870ad2c>
- testtext=zzz
universal_newlines參數:
如果把universal_newlines 設置成True,則子進程的stdout和stderr被視為文本對象,並且不管是*nix的行結束符('/n'),還是老mac格式的行結束符('/r' ),還是windows 格式的行結束符('/r/n' )都將被視為 '/n' 。
startupinfo和creationflags參數:
如果指定了startupinfo和creationflags,將會被傳遞給后面的CreateProcess()函數,用於指定子程序的各種其他屬性,比如主窗口樣式或者是子進程的優先級等。(僅限Windows)
二、subprocess.PIPE
- subprocess.PIPE
一個可以被用於Popen的stdin 、stdout 和stderr 3個參數的特輸值,表示需要創建一個新的管道。
- subprocess.STDOUT
一個可以被用於Popen的stderr參數的輸出值,表示子程序的標准錯誤匯合到標准輸出。
實例:
- >>>p=subprocess.Popen("df -h",shell=True,stdout=subprocess.PIPE)
- >>>out=p.stdout.readlines()
- >>>out
- [b'Filesystem Size Used Avail Capacity Mounted on\n', b'/dev/ad0s1a 713M 313M 343M 48% /\n', b'devfs 1.0K 1.0K 0B 100% /dev\n', b'/dev/ad0s1e 514M 2.1M 471M 0% /tmp\n', b'/dev/ad0s1f 4.3G 2.5G 1.4G 64% /usr\n', b'/dev/ad0s1d 2.0G 121M 1.7G 6% /var\n'
- >>> for line in out:
- ... print line.strip()
- ...
- Filesystem Size Used Avail Capacity Mounted on
- /dev/ad0s1a 713M 313M 343M 48% /
- devfs 1.0K 1.0K 0B 100% /dev
- /dev/ad0s1e 514M 2.1M 471M 0% /tmp
- /dev/ad0s1f 4.3G 2.5G 1.4G 64% /usr
- /dev/ad0s1d 2.0G 121M 1.7G 6% /var
stdout可以使用read(),readline(),readlines()等方法
三、方便的函數
1、subprocess.call
- subprocess.call (*popenargs , **kwargs )
執行命令,並等待命令結束,再返回子進程的返回值。參數同Popen,查看/usr/lib/python2.7/subprocess.py
去掉文檔,其實是這樣的:
- def call(*popenargs, **kwargs):
- return Popen(*popenargs, **kwargs).wait()
- >>> subprocess.call('ifconfig',shell=True)
2、subprocess.check_call
- subprocess.check_call (*popenargs , **kwargs )
執行上面的call命令,並檢查返回值,如果子進程返回非0,則會拋出CalledProcessError異常,這個異常會有個returncode
屬性,記錄子進程的返回值。
- def check_call(*popenargs, **kwargs):
- retcode = call(*popenargs, **kwargs)
- if retcode:
- cmd = kwargs.get("args")
- raise CalledProcessError(retcode, cmd)
- return 0
- >>> subprocess.check_call('ifconfig')
- >>> subprocess.call('noifconfig')
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "/usr/local/lib/python2.7/subprocess.py", line 493, in call
- return Popen(*popenargs, **kwargs).wait()
- File "/usr/local/lib/python2.7/subprocess.py", line 679, in __init__
- errread, errwrite)
- File "/usr/local/lib/python2.7/subprocess.py", line 1228, in _execute_child
- raise child_exception
- OSError: [Errno 2] No such file or directory
異常子進程里拋出的異常,會在父進程中再次拋出。並且,異常會有個叫child_traceback的額外屬性,這是個包含子進程錯誤traceback信息的字符串。遇到最多的錯誤回是 OSError,比如執行了一個並不存在的子程序就會產生OSError。另外,如果使用錯誤的參數調用Popen,會拋出ValueError。當子程序返回非0時,check_call()還會產生CalledProcessError 異常。
安全性不像其他的popen函數,本函數不會調用/bin/sh來解釋命令,也就是說,命令中的每一個字符都會被安全地傳遞到子進程里。
3、check_output
- check_output()執行程序,並返回其標准輸出.
- def check_output(*popenargs, **kwargs):
- process = Popen(*popenargs, stdout=PIPE, **kwargs)
- output, unused_err = process.communicate()
- retcode = process.poll()
- if retcode:
- cmd = kwargs.get("args")
- raise CalledProcessError(retcode, cmd, output=output)
- return output
- p=subprocess.check_output('ifconfig')
結果是所有行/n分割的一個字符串可以直接print出來 這里開始
4、Popen對象
產生對象
- p=subprocess.Popen("df -h",shell=True,stdout=subprocess.PIPE)
- >>> dir(p)
Popen對象有以下方法:
- Popen.poll()
檢查子進程是否已結束,設置並返回returncode屬性。
- >>> p.poll()
- 0
- Popen.wait()
等待子進程結束,設置並返回returncode屬性。
- >>> p.wait()
- 0
注意: 如果子進程輸出了大量數據到stdout或者stderr的管道,並達到了系統pipe的緩存大小的話,子進程會等待父進程讀取管道,而父進程此時正wait着的話,將會產生傳說中的死鎖,后果是非常嚴重滴。建議使用communicate() 來避免這種情況的發生。
- Popen.communicate(input=None)
和子進程交互:發送數據到stdin,並從stdout和stderr讀數據,直到收到EOF。等待子進程結束。可選的input如有有的話,要為字符串類型。
此函數返回一個元組: (stdoutdata , stderrdata ) 。
注意,要給子進程的stdin發送數據,則Popen的時候,stdin要為PIPE;同理,要可以接收數據的話,stdout或者stderr也要為PIPE。
- p1=subprocess.Popen('cat /etc/passwd',shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
- >>> p2=subprocess.Popen('grep 0:0',shell=True,stdin=p1.stdout,stdout=subprocess.PIPE)
注意:讀到的數據會被緩存在內存里,所以數據量非常大的時候要小心了。
- >>> p.communicate()
- (b'Filesystem Size Used Avail Capacity Mounted on\n/dev/ad0s1a 713M 313M 343M 48% /\ndevfs 1.0K 1.0K 0B 100% /dev\n/dev/ad0s1e 514M 2.1M 471M 0% /tmp\n/dev/ad0s1f 4.3G 2.5G 1.4G 64% /usr\n/dev/ad0s1d 2.0G 121M 1.7G 6% /var\n', None)
- 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的進程號。
- >>> p.pid
- 22303
- Popen.returncode
子程序的返回值,由poll()或者wait()設置,間接地也由communicate()設置。
如果為None,表示子進程還沒終止。
如果為負數-N的話,表示子進程被N號信號終止。(僅限*nux)
用subprocess來代替其他函數都可以用subprocess來完成,我們假定是用 “from subprocess import *” 來導入模塊的:
代替shell命令:
- p=`ls -l`
等效於
- p=Popen(['ls','-l'],stdout=PIPE).communicate()[0]
代替shell管道:
- p=`dmesg | grep cpu`
等效於
- p1=Popen(['dmesg'],stdout=PIPE)
- p2=Popen(['grep','cpu'],stdin=p1.stdout,stdout=PIPE)
- output = p2.communicate()[0]
- output
- cpu0: <ACPI CPU> on acpi0\nacpi_throttle0: <ACPI CPU Throttling> on cpu0\n
- >>> p1=subprocess.Popen('cat /etc/passwd',shell=True,stdout=subprocess.PIPE)
- >>> p2=subprocess.Popen('grep 0:0',shell=True,stdin=p1.stdout,stdout=subprocess.PIPE)
- >>> p3=subprocess.Popen("cut -d ':' -f 7",shell=True,stdin=p2.stdout,stdout=subprocess.PIPE)
- >>> print p3.stdout.read()
代替os.system()
- lsl = os.system('ls '+'-l')
這個是一個返回狀態
等效於
- p=Popen('ls -l', shell=True)
- lsl=os.waitpid(p.pid,0)[1]
注意:
通常並不需要用shell來調用程序。用subprocess可以更方便地得到子程序的返回值。
其實,更真實的替換是:
- try:
- retcode = call(“mycmd” + ” myarg”, shell=True)
- if retcode < 0:
- print >>sys.stderr, “Child was terminated by signal”, -retcode
- else:
- print >>sys.stderr, “Child returned”, retcode
- except OSError, e:
- print >>sys.stderr, “Execution failed:”, 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"])
返回值處理:
- 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”
以上這篇python中的subprocess.Popen()使用詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持我們。
本文轉載於:豬笨是念來過倒