## 概述
subprocess模塊中只定義了一個類:Popen。可以使用Popen來創建進程,并與進程進行復雜的交互。它的構造函數如下。
---
### Popen使用示例
示例1,使用shlex.split先對參數進行處理
```
>>> 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!
```
示例2. 直接使用列表作為參數
```
Popen(['/bin/sh', '-c', args[0], args[1], ...])
```
示例3. 這個上下文管理器在python3中才可以使用
```
with Popen(["ifconfig"], stdout=PIPE) as proc:
log.write(proc.stdout.read())
```
---
### subprocess.Popen參數
```
subprocess.Popen(args, bufsize=0, executable=None, shell=False, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False,cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
```
`參數args`:**字符串**或者**序列類型**(如:list,元組),用于指定進程的可執行文件及其參數。如果是序列類型,第一個元素通常是可執行文件的路徑。
`參數bufsize`:指定緩沖。
`參數executable`: 用于指定可執行程序。一般情況下我們通過args參數來設置所要運行的程序。如果將參數shell設為True,executable將指定程序使用的shell。
`參數stdin, stdout, stderr`: 分別表示程序的標準輸入、輸出、錯誤句柄。他們可以是PIPE,文件描述符或文件對象,也可以設置為None,表示`從父進程繼承`。
`參數preexec_fn`: 只在Unix平臺下有效,用于指定一個可執行對象(callable object),它將在子進程運行之前被調用。
`參數shell`: 設為true,程序將通過shell來執行。
`參數cwd`: 用于設置子進程的當前目錄。
`參數env`: 是字典類型,用于指定子進程的環境變量。如果env = None,子進程的環境變量將從父進程中繼承。
`參數Universal_newlines`: 不同操作系統下,文本的換行符是不一樣的。如:windows下用'\r\n'表示換,而Linux下用'\n'。如果將此參數設置為True,Python統一把這些換行符當作'\n'來處理。
---
### subprocess.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.stdin
如果在創建Popen對象是,參數stdin被設置為PIPE,Popen.stdin將返回一個文件對象用于策子進程發送指令。否則返回None。
* Popen.stdout
如果在創建Popen對象是,參數stdout被設置為PIPE,Popen.stdout將返回一個文件對象用于策子進程發送指令。否則返回None。
* Popen.stderr
如果在創建Popen對象是,參數stdout被設置為PIPE,Popen.stdout將返回一個文件對象用于策子進程發送指令。否則返回None。
* Popen.pid
獲取子進程的進程ID。
* Popen.returncode
獲取進程的返回值。如果進程還沒有結束,返回None。
下面是一個非常簡單的例子,來演示supprocess模塊如何與一個控件臺應用程序進行交互。
---
### supprocess其他
* subprocess.PIPE
在創建Popen對象時,subprocess.PIPE可以作為初始化stdin,stdout或stderr參數。表示與子進程通信的標準流。
* subprocess.STDOUT
創建Popen對象時,用于初始化stderr參數,表示將錯誤通過標準輸出流輸出。
* subprocess.call(\*popenargs, \*\*kwargs)
運行命令。該函數將一直等待到子進程運行結束,并返回進程的`returncode`。如果子進程不需要進行交互,就可以使用該函數來創建。
> 報錯是shell錯誤,無法捕捉
* subprocess.check_call(\*popenargs, \*\*kwargs)
與subprocess.call(\*popenargs, \*\*kwargs)功能一樣,只是如果子進程返回的returncode不為0的話,將觸發CalledProcessError異常。在異常對象中,包括進程的`returncode`信息。
> 報錯是python錯誤,在python中可以捕捉
### 設置超時時間
通過改寫,設置腳本執行的超時時間
~~~
#!/usr/bin/env python
# coding=utf-8
import shlex
import datetime
import subprocess
import time
def execute_command(cmdstring, cwd=None, timeout=None, shell=False):
"""執行一個SHELL命令封裝了subprocess的Popen方法, 支持超時判斷,支持讀取stdout和stderr
參數:
cwd: 運行命令時更改路徑,如果被設定,子進程會直接先更改當前路徑到cwd
timeout: 超時時間,秒,支持小數,精度0.1秒
shell: 是否通過shell運行
Returns: return_code
Raises: Exception: 執行超時
"""
if shell:
cmdstring_list = cmdstring
else:
cmdstring_list = shlex.split(cmdstring)
if timeout:
end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
#沒有指定標準輸出和錯誤輸出的管道,因此會打印到屏幕上;
sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE,shell=shell,bufsize=4096)
#subprocess.poll()方法:檢查子進程是否結束了,如果結束了,設定并返回碼,放在subprocess.returncode變量中
while sub.poll() is None:
time.sleep(0.1)
if timeout:
if end_time <= datetime.datetime.now():
raise Exception("Timeout:%s"%cmdstring)
return str(sub.returncode)
if __name__=="__main__":
print execute_command("sleep 5", timeout=2)
~~~
---
### Popen的wait方法和communicate方法比較
Popen的wait方法之后程序一直沒有返回,原因是wait是有可能產生死鎖。
使用 subprocess 模塊的 Popen 調用外部程序,如果 stdout 或 stderr 參數是 PIPE,并且程序輸出超過操作系統的 pipe size時,如果使用 Popen.wait() 方式等待程序結束獲取返回值,會導致死鎖,程序卡在 wait() 調用上。
`ulimit -a` 看到的 pipe size 是 4KB,那只是每頁的大小,查詢得知 Linux 默認的 pipe size 是 `64KB`。
#### 對wait方法和communicate方法進行測試
```
#!/usr/bin/env python
# coding: utf-8
import subprocess
def test(size):
print 'start'
cmd = 'dd if=/dev/urandom bs=1 count=%d 2>/dev/null' % size
p = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
p.communicate()
#p.wait()
print 'end'
```
測試結果
p.wait工作時,64kb時工作正常,64k+1時,程序卡住,產生死鎖。
p.communicate工作時,兩種情況下都可以正常工作。
```
# 64KB
test(64 * 1024)
# 64KB + 1B
test(64 * 1024 + 1)
```
官方文檔里推薦使用 `Popen.communicate()`。這個方法會把輸出放在`內存`,而不是管道里,所以這時候上限就和內存大小有關了。而且如果要獲得程序返回值,可以在調用 Popen.communicate() 之后取 Popen.returncode 的值。
結論
如果使用 subprocess.Popen,應該使用 Popen.communicate() 來等待外部程序執行結束。
---
### subprocess的兩種方法 阻塞和非阻塞
1)如果想調用之后直接阻塞到子程序調用結束
```
#start and block until done
import shlex, subprocess
cmd = 'dd if=/dev/zero of=bigfile bs=1M count=32'
cmd = shlex.split(cmd)
subprocess.call(cmd)
```
2)非阻塞的時候方式
```
#start and process things, then wait
from subprocess import Popen,PIPE
cmd = 'dd if=/dev/zero of=bigfile bs=1M count=32'
p = Popen(args=cmd, shell=True, stdout=PIPE,stderr=PIPE)
print p.communicate()
print 'end...'
```
- 前言
- 環境搭建
- pypi
- 打包
- Python 2 和 Python 3 的版本之間差別
- 項目
- 第一部分
- 第1章 基礎
- Python安裝
- python代碼文件類型
- python對象
- 核心數據類型
- 核心數據類型--整型和浮點型
- 核心數據類型--字符串
- str.format
- 核心數據類型--列表
- 核心數據類型--元組
- 核心數據類型--字典
- 核心數據類型--集合
- 核心數據類型--文件對象
- 調用bash
- 標準輸入輸出
- str-repr
- 字符編碼
- 迭代器和生成器
- 第2章 語句和語法
- 賦值語句
- if語句
- while語句
- for語句
- assert
- 第3章 函數
- 函數作用域
- 工廠函數
- 內置函數
- 遞歸
- 嵌套作用域和lambda
- 參數傳遞
- 函數式編程
- property可寫與可讀
- 第5章 模塊
- 模塊導入
- 模塊命名空間
- 相對導入和絕對導入
- 模塊重載
- 在模塊中隱藏數據
- 過渡性重載
- 第6章 類
- 面向對象還是面向過程?
- 構造函數 析構函數
- call
- 運算符重載
- str()
- 待定
- 即時生成屬性
- 多態
- 線程和進程
- thread模塊
- threading模塊
- threading線程鎖
- 糖果機
- multiprocessing
- 阻塞非阻塞同步異步
- 單線程和多線程對比
- 生產者消費者模型
- 第二部分
- 獲取系統資源信息
- 獲取進程所占的物理內存
- dmidecode獲取系統信息
- 網絡編程
- 網絡基礎
- python中的套接字
- socket模塊
- 第三部分 高級功能
- 閉包入門
- 閉包的應用
- 裝飾器入門
- 裝飾器應用
- 第四部分 項目實戰
- graphite
- 模塊
- collections
- datetime
- Enum
- faker
- fabric
- fileinput
- fire
- fnmatch
- getpass
- glob
- hashlib
- heapq
- json模塊
- log
- os
- Paramiko
- parser
- platform
- pyyaml
- Queue
- random
- re
- 特殊符號和字符
- re模塊
- shelves
- subprocess
- time
- urllib_urllib2_requests
- urllib urllib2
- requests
- 標準模塊ConfigParser
- 擴展模塊Mysqldb
- 擴展模塊dns
- 擴展模塊request
- uuid
- cacheout 緩存庫
- delorean 時間
- 附錄
- 內置函數
- python實現各種排序算法
- 常見報錯
- pymongo
- pyrocksdb
- 常用
- ERROR