星期三, 2月 02, 2022

subprocess.check_output 小記

subprocess 是 python 一個很好用的模組,提供執行子程序的各種方式,最常用的就是執行,然後取得輸出結果。

import subprocess

try:
  cmd_output = subprocess.check_output("ls")
except subprocess.CalledProcessError as ex:
  cmd_output = ex.stdout

print("Command output:")
print(cmd_output)

接下來,因為有些指令是之前已經串好,已經加上 pipe 的了,所以會這樣用

cmd_output = subprocess.check_output("ls /usr/bin | grep python")

但你會發現 subprocess.check_output 拋出一個例外

FileNotFoundError: [Errno 2] No such file or directory: 'ls /usr/bin | grep python': 'ls /usr/bin | grep python'

這到底怎麼回事?明明就有 ls 這個執行檔?

原因很簡單,因為 subprocess.check_output 是把第一個參數視為一個檔案,所以有 pipe 的情況,他是不認可的。這時候可以加上 shell=True,讓 subprocess.check_output 知道執行第一個參數時,要先啟動 shell 來執行第一個參數裡的命令。

cmd_output = subprocess.check_output("ls /usr/bin | grep python", shell=True)

在取得輸出結果以後,cmd_output 是一個 byte 型態的變數,換言之,他不是字串。這時需要 decode 幫忙,轉換為字串:

cmd_output = cmd_output.decode('utf-8')

最後要分享的是將錯誤輸出到 null 裝置的方法,一般 bash script 裏面會用這樣的方式來將錯誤導向到 null 裝置,以避免出現不必要的輸出:

ls /usr/bin/foo 2>/dev/null

在呼叫 subprocess.check_output 時,可以帶入一個參數 stderr,而無需把 >/dev/null 寫在第一個參數裡。

cmd_output = subprocess.check_output("ls /usr/bin/foo", stderr=subprocess.DEVNULL, shell=True)

以上是自己在把 shell script 轉換為 python 時遇到的幾種狀況,因為已經遇到幾次,總是會多花一點時間回想,這次在這邊紀錄下來,以免之後又忘掉。

沒有留言: