Day 2. 執行外部指令

碎碎念:這幾天瑣事好多,結果自行放假兩天。或許之後六日也都停更感覺比較不會那麼累orz.

早在還沒碰python之前。有時候載別人套件的source code下來自己compile時,會發現他們用了一些python的scripts來輔助使用者安裝。
當時想說,既然會額外用python來做這些輔助作業,而不是使用大多Unix類作業系統都會有的shell script來做這件事(雖然shell又分好幾種就是...),想必有他獨到的地方。

所以研究怎麼利用python來做這些事情就變成個人優先想了解的部分。

subprocess

和前面與其他語言binding的部分不同,呼叫外部的指令好像主要都是用subprocess來做了。
不論是從網路上查到的資料,或是python的官方文件來看。
(This module intends to replace several older modules and functions)

能夠開啟一個child process的方法大致上有:

  1. subprocess.run (python 3.5 ~)
  2. subprocess.call
  3. subprocess.check_call / subprocess.check_output
  4. subprocess.Poepn

從找到的這篇來看What's the difference between Python's subprocess.call and subprocess.run,
可以知道run是從3.5版開始才有的東西,目的也許是提供比較全面的功能,
整合目前的call / check_call / check_output。
上面都是高階的函式,Popen則是低階的。

subprocess.run

subprocess.run的用法大致如下:

import subprocess as sp  

# args: 預設型別是list.  
sp.run(['ls', '-l'])    
# 將 args 作為 /bin/bash 的參數  
sp.run( 'ls -l', shell=True )    
# 取得output, 用pipe把資料轉到程式內:
out = sp.run(['ls', '-l'], stdout=sp.PIPE)   

# 讀檔:  
with open( './data.txt', 'r' ) as file:  
    sp.run(['cat'],  stdin=file)  
# 若要從程式內輸入資料,type必須為bytes:  
msg = 'hello, world\n'  
sp.run(['cat'], input=msg.encode())

還有其他沒寫到的有

  1. run( ..., check = True ): 效力等同於 check_call (除了沒input參數)
  2. run( ..., timeout = sec ): 執行時間過多久timeout.
  3. run預設的stdin, stdout, stderr都是None.

subprocess.call

  1. subprocess.call基本上和run差不多,主要差在少了input這個參數不能用而已。
  2. subprocess.check_call 和call差不多,不同點在回傳狀態不為0則會丟例外。
  3. subprocess.check_output 和check_call差不多,差在他會回傳stdout的結果。
  4. subprocess.Popen

Popen提供許多管理subprocess的低階方法,這也代表他有很多參數可以調整。
這邊只複製別人整理需要用到的、和run, call這些方法不同的地方。

Popen的Constructor的參數和run其實差不多。
以前的文章中有人提到,其實call和Popen的差別只在於: call在construct結束後會呼叫Popen.wait().

但單獨使用Popen.wait的時候有一點要小心的是,由於他會等程式回傳正常結束的狀態值,所以如果前面有指定stdout=PIPE, stderr=PIPE可能會造成deadlock。
因此若要取得stdout, stderr, 最好是呼叫communicate來做這件事情。

import subprocess as sp  

# Input from file:  
with open('./data.txt', 'r') as file:  
    p = sp.Popen( ['cat'], stdin=file, stdout=sp.PIPE, stderr=sp.PIPE )  
    out, err = p.communicate()  

# Input from python:  
p = sp.Popen( ['cat'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE )  
out, err = p.communicate( input=b'hello, world\n' )

參考資料:

備註:
不知為何,總覺得python真的還滿好玩的。

results matching ""

    No results matching ""