Cheatsheet for Python subprocess

有感於每次使用 subprocess 都要看好多資料,因此將 Python Documents 重點整理及各種情況下如何使用 subprocess 詳細記錄並解說。

subprocess module introduction

  • subprocess.call
  • subprocess.check_call
  • subprocess.check_output
  • subprocess.Popen

subprocess.call

Run the command described by args. Wait for command to complete, then return the returncode attribute.

也就是說,我們的 code 會 等到指令執行結束才回傳結果,用這個例子來試試看:

1
subprocess.call(['sleep', '1'])

結果會在 1 秒後回傳 0,這個 0 有著特殊意義,它代表著 return code,程式執行後的狀態,而 0 正代表著程式正確無誤的執行返回,也就是 success 的意思。

這邊需要注意的是,arg 代入的參數是 list 型態,如果需要帶入字串的話,可以加上一個參數,像以下例子:

1
subprocess.call('sleep 1', shell=True)

shell=True 表示會呼叫一個 /bin/sh 來執行這條指令,但可能會因此而發生 Command Injection 的問題,所以並不推薦這麼做。

subprocess.check_call

Run command with arguments. Wait for command to complete. If the return code was zero then return, otherwise raise CalledProcessError.

而如同剛剛所說,return code0 通常表示正常執行,因為 return code 又稱為 Exit Status,因此若回傳的值不是 0 的話,subprocess.check_call 會拋出 Excecption。

也因為此 Function 的特性,所以我們會在不需執行結果,只需要執行狀態的時候使用它

1
2
3
4
if not subprocess.check_call(['ls']):
print('Command Success Execute')
else:
print('Oops, something error')

subprocess.check_output

Run command with arguments and return its output as a byte string.

對我而言,check_output 則是個很實用的 function,它會直接回傳執行後的輸出,也就是程式執行時被打印到 stdout 的內容。不過值得注意的一點是,與上一個 function 相同,如果 return code 不是 0 也會拋出例外。

舉個例子,先試著讓他拋出例外:

1
2
3
4
try:
output = subprocess.check_output('exit 1', shell=True)
except subprocess.CalledProcessError:
print('Exception handled')

然後則是在 return code 是 0 的狀態:

1
2
3
4
5
try:
output = subprocess.check_output(['ls'])
except subprocess.CalledProcessError:
print('Exception handled')
# 然後試著印出 output 吧

subprocess.Popen

Execute a child program in a new process. On Unix, the class uses os.execvp()-like behavior to execute the child program.

這是我最常使用的 function,有時候我們不只需要 stdout 的結果,同時還會需要接收 stderr 的結果。

舉個例子,先寫段 code 來達成一個簡易的功能,如果輸入過短則把錯誤訊息輸出到 stderr, 否則便把 'Hello name' 輸出到 stdout,最後再讓我們使用 subprocess 呼叫這段程式碼來看看它的行為:

1
2
3
4
5
6
7
8
#! /usr/bin/python
import sys

name = input()
if len(name) < 3:
print >> sys.stderr, "Name is too short"
else:
print "Hello %s" % name

首先是 input 長度正常的結果:

1
2
3
4
from subprocess import Popen, PIPE
p = Popen(['python', 'test.py'], stdout=PIPE, stderr=PIPE, stdin=PIPE)
stdout, stderr = p.communicate(input='aweimeow\n')
# stdout = 'Hello aweimeow\n'

再來是 input 長度過短的結果:

1
2
3
4
from subprocess import Popen, PIPE
p = Popen(['python', 'test.py'], stdout=PIPE, stderr=PIPE, stdin=PIPE)
stdout, stderr = p.communicate(input='a\n')
# stderr = 'Name is too short\n'

如此一來便可以輕鬆的接到 stdoutstderr 並處理它了。