1. 程式人生 > >python模組系列之

python模組系列之

subprocess – 建立附加程序
subprocess模組提供了一種一致的方法來建立和處理附加程序,與標準庫中的其它模組相比,提供了一個更高階的介面。用於替換如下模組:
os.system() , os.spawnv() , os和popen2模組中的popen()函式,以及 commands().

1. 執行外部命令
subprocess.call(command) 方法
subprocess的call方法可以用於執行一個外部命令,但該方法不能返回執行的結果,只能返回執行的狀態碼: 成功(0) 或 錯誤(非0)
call()方法中的command可以是一個列表,也可以是一個字串,作為字串時需要用原生的shell來執行:

import subprocess
#執行 df -hl 命令
#方法1:
>>> subprocess.call(['ls','-l'])
total 8
drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
0
#方法2:
>>> subprocess.call("ls -l",shell=True)
total 8
drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
drwxrwxr-x 2
ws ws 4096 Feb 19 10:09 tmp 0

如上例項所示,雖然我們能看到執行的結果,但實際獲取的值只是狀態碼

>>> output = subprocess.call("ls -l",shell=True)
total 8
drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
>>> print(output)
0

2. 錯誤處理
subprocess.check_call() 方法
我們說過call執行返回一個狀態碼,我們可以通過check_call()函式來檢測命令的執行結果,如果不成功將返回 subprocess.CalledProcessError 異常

>>> try:
        subprocess.check_call("ls -t", shell=True)
    except subprocess.CalledProcessError as err:
        print("Command Error") 

/bin/sh: lt: command not found
Command Error

3. 捕獲輸出結果
subprocess.check_output() 方法

call()方法啟動的程序,其標準輸入輸出會繫結到父程序的輸入和輸出。呼叫程式無法獲取命令的輸出結果。但可以通過check_output()方法來捕獲輸出。

# 以下測試為python3.4下執行結果
>>> output=subprocess.check_output("ls -l",shell=True)
>>> output
b'total 8\ndrwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem\ndrwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp\n'
>>> print(output.decode('utf-8'))
total 8
drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp

以下例子將chek_output()方法執行命令異常時的錯誤捕獲,而避免輸出到控制檯.

try:
    output = subprocess.check_output("lT -l", shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as err:
    print("Command Error", err)

# 執行結果
Command Error Command 'lT -l' returned non-zero exit status 127

直接處理管道
subprocess.Popen()方法

函式call(), check_call() 和 check_output() 都是Popen類的包裝器。直接使用Popen會對如何執行命令以及如何處理其輸入輸出有更多控制。如通過為stdin, stdout和stderr傳遞不同的引數。

  1. 與程序的單向通訊
    通過Popen()方法呼叫命令後執行的結果,可以設定stdout值為PIPE,再呼叫communicate()獲取結果
    返回結果為tuple. 在python3中結果為byte型別,要得到str型別需要decode轉換一下

輸出結果(讀)

# 直接執行命令輸出到螢幕
>>> subprocess.Popen("ls -l",shell=True)
<subprocess.Popen object at 0x7febd4175198>
>>> total 12
drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
-rw-rw-r-- 1 ws ws    8 Feb 25 10:38 test
drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp

# 不輸出到螢幕,輸出到變數
>>> proc = subprocess.Popen(['echo','"Stdout"'],stdout=subprocess.PIPE)
# communicate返回標準輸出或標準出錯資訊
>>> stdout_value = proc.communicate()
>>> stdout_value
(b'"Stdout"\n', None)

>>> proc = subprocess.Popen(['ls','-l'],stdout=subprocess.PIPE)
>>> stdout_value = proc.communicate()
>>> stdout_value
(b'total 8\ndrwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem\ndrwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp\n', None)
>>>
>>> print((stdout_value[0]).decode('utf-8'))
total 8
drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp

#將結果輸出到檔案
>>> file_handle = open("/home/ws/t.log",'w+')
>>> subprocess.Popen("ls -l",shell=True,stdout=file_handle)
t.log:
drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
-rw-rw-r-- 1 ws ws    8 Feb 25 10:38 test
-rw-rw-r-- 1 ws ws    0 Feb 25 11:24 t.log
drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp

2 與程序的雙向通訊

>>> proc = subprocess.Popen('cat', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> msg = 'Hello world'.encode('utf-8')
# 寫入到輸入管道
>>> proc.stdin.write(msg)
11
>>> stdout_value = proc.communicate()
>>> stdout_value
(b'Hello world', None)

# 在需要進行相互互動的輸入輸出過程也可以使用shtin來實現
# 以下實現開啟python3的終端,執行一個print命令
>>>proc = subprocess.Popen(['python3'],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE,)
>>>proc.stdin.write('print("helloworld")'.encode('utf-8'))
>>>out_value,err_value=proc.communicate()
>>>print(out_value)
>>> print(out_value)
b'helloworld\n'
>>> print(err_value)
b''

Popen.communicate()方法用於和子程序互動:傳送資料到stdin,並從stdout和stderr讀資料,直到收到EOF。等待子程序結束。

捕獲錯誤輸出

>>> proc = subprocess.Popen(['python3'],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE,)
>>> proc.stdin.write('print "helloworld"'.encode('utf-8'))
18
>>> out_value,err_value=proc.communicate()
>>> out_value
b''
>>> print(err_value.decode('utf-8'))
  File "<stdin>", line 1
    print "helloworld"
                     ^
SyntaxError: Missing parentheses in call to 'print'

Popen其它方法

  1. Popen.pid 檢視子程序ID
  2. Popen.returncode 獲取子程序狀態碼,0表示子程序結束,None未結束

    在使用Popen呼叫系統命令式,建議使用communicate與stdin進行互動並獲取輸出(stdout),這樣能保證子程序正常退出而避免出現殭屍程序。看下面例子

>>> proc = subprocess.Popen('ls -l', shell=True, stdout=subprocess.PIPE)
# 當前子程序ID
>>> proc.pid
28906
# 返回狀態為None,程序未結束
>>> print(proc.returncode)
None
# 通過communicate提交後
>>> out_value = proc.communicate()
>>> proc.pid
28906
# 返回狀態為0,子程序自動結束
>>> print(proc.returncode)
0