?本篇文章難度較大,閱讀時間較長,歷劫進階,,,
Android系統測驗或App測驗程序中,
有的Android終端設備由于磁盤太小,未開啟隨系統自啟動logcat序列log,
即未實時在后臺截取Logcat log,所以需要測驗人員進行手動截取Logcat Log,
這種情況下, 一般我們是直接cmd, adb logcat -v threadtime > D:\logcat.txt,
通過以上命令來進行手動截取Logcat 日志,
我曾經考慮過一種更方便快捷的截取一鍵截取Logcat的Python腳本,
直接點擊運行該Python腳本即可,無需手動敲以上命令!
本周重點介紹的是通過是subprocess.Popen()類進行命令列的呼叫,銷毀(殺行程)!
準備階段
- adb logcat -v threadtime > D:\logcat_20200310_101112.txt可以列印按執行緒時間log并保存到一個檔案,
- 由于adb logcat命令是一個持續輸出的命令,
它如果沒被銷毀(殺行程),會一直持續截取下去, - os.system()函式和os.popen()函式并不適合做子行程銷毀,
這時候就要考慮更高端的subprocess.Popen()類來實作行程銷毀,
subprocess.Popen()類介紹
Python官方推薦使用subprocess.Popen()類來調起其他命令(即創建子行程),
其具備優質的子行程管理(創建,銷毀,進行標準輸入,進行標準輸出,進行錯誤輸出等)特性,
為什么說他是一個類,而不是一個函式,請看原始碼截圖:

既然是類,則首先先看下這個類的初始化的源代碼:
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True,
shell=False, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, encoding=None, errors=None, text=None):
我們只需要了解以下幾個初始化的引數即可:
<style>table th:first-of-type { width: 100px }</style>| 引數 | 釋義 | 引數初始化舉例 |
|---|---|---|
| args | 需要子行程執行的命令或者命令陣列 | args="adb reboot" args=["adb", "reboot"] |
| stdin | standard input 對某個子行程的標準輸入物件進行定義 如果我們的子行程后續是不需要進行互動式輸入的: stdin=None (默認值) 如果我們的子行程是需要進行互動式輸入的: stdin=subprocess.PIPE |
stdin=None 或 stdin=subprocess.PIPE |
| stdout | standard output 對某個子行程的標準輸出進行定義, 如果我們不需要捕捉標準輸出則:stdout=None(默認值) 如果需要對標準輸出進行捕捉及分析:stdout=subprocess.PIPE 如果需要對標準輸出進行存盤:stdout=open("log.txt","wb") |
stdout=None 或 stdout=subprocess.PIPE 或 stdout=open("log.txt","wb") |
| stderr | standard error 對某個子行程的標準例外錯誤提示資訊進行定義, 如果我們不需要捕例外錯誤資訊則:stderr=None(默認值) 如果需要對例外錯誤進行捕捉及分析:stderr=subprocess.PIPE 如果需要對例外錯誤進行存盤:stderr=open("log.txt","wb") |
stderr=None 或 stderr=subprocess.PIPE 或 stderr=open("log.txt","wb") |
| shell | 如果子行程是必須通過cmd /c 或者/system/bin/sh -c 來執行的,則必須shell=True, 我們一般的命令,請直接用shell=False(默認值)即可, 比如如果在windows上執行adb命令, 如果shell=True, 將額外呼叫起cmd行程,然后是adb行程 如果shell=False, 將只會呼叫起adb行程(建議用這個) |
shell=False 或 shell=True |
是類,則肯定需要實體化成物件后才能進行具體的操作,
且是類,則就涉及類的屬性(變數)定義,方法(函式)定義,
我們假定初始化后的物件名是p_obj, 釋義如下:
| 屬性(變數)串列 | 釋義 |
|---|---|
| p_obj.pid | 子行程物件的:行程pid |
| p_obj.returncode | 子行程物件的:當前的狀態 None:代表子行程命令還在執行中,無法獲取其狀態 0:代表正常執行了子行程命令并正常結束 1:代表例外結束,比如被kill或者terminate了 |
| p_obj.args | 子行程物件執行的:具體命令 |
<style>table th:first-of-type { width: 100px }</style>
| 方法(函式)串列 | 釋義 |
|---|---|
| 方法(函式)串列 | 釋義 |
| p_obj.kill() | Windows系統上,與terminate()功能相同,都是殺掉行程 Linux系統上,發送的是SIGTKILL信號,即強制殺掉你這個行程,類似kill -9 萬能建議用法(順序執行以下2個函式): p_obj.terminate() p_obj.kill() |
| p_obj.terminate() | Windows系統上,與terminate()功能相同,都是殺掉行程 Linux系統上,發送的是SIGTERM信號,即通知子行程趕緊自行結束,類似kill -15 萬能用法(順序執行以下2個函式): p_obj.terminate() p_obj.kill() |
Python批處理腳本形式
# coding=utf-8
import os
import subprocess
from datetime import datetime
command = "adb logcat -v threadtime"
now_time = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = "Logcat_" + now_time + ".txt"
hf = open("%s" % log_file, "wb")
# 因為沒指定具體路徑,默認就是在當前腳本運行的路徑下創建這個log_file
# 開始執行adb 命令
p_obj = subprocess.Popen(
args=command,
stdin=None, stdout=hf,
stderr=hf, shell=False)
print("Logcat catching...")
# 持續詢問是否需要停止截取
judgement = input("If you think it is enough, please input Y:")
while (judgement != "Y" and judgement != "y"): ###這里必須是and.
print("Invalid input, please input Y")
judgement = input("If you think it is enough, please input Y:")
else:
if (judgement == "Y" or judgement == "y"): # 如果收到停止Yes信號,則開始結束截取
p_obj.terminate()
p_obj.kill()
hf.close()
print("Logcat log has saved to %s" % os.path.abspath(log_file))
os.system("pause")
Python面向程序函式形式
本次優化,將COMMAND作為常量,常量一般是編碼風格是全部大寫,
涉及檔案open讀寫的,盡量用with形式,
# coding=utf-8
import os
import subprocess
from datetime import datetime
COMMAND = "adb logcat -v threadtime"
def catch_logcat():
now_time = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = "Logcat_" + now_time + ".txt"
# 因為沒指定具體路徑,默認就是在當前腳本運行的路徑下創建這個log_file
# 開始執行adb 命令
with open("%s" % log_file, "wb") as hf:
p_obj = subprocess.Popen(
args=COMMAND,
stdin=None, stdout=hf,
stderr=hf, shell=False)
print("Logcat catching...")
# 持續詢問是否需要停止截取
judgement = input("If you think it is enough, please input Y:")
while (judgement != "Y" and judgement != "y"): ###這里必須是and.
print("Invalid input, please input Y")
judgement = input("If you think it is enough, please input Y:")
else:
if (judgement == "Y" or judgement == "y"): # 如果收到停止Yes信號,則開始結束截取
p_obj.terminate()
p_obj.kill()
return os.path.abspath(log_file)
print("Logcat log has saved to %s" % catch_logcat())
os.system("pause")
Python面向物件形式
本次優化:
- 在類的初始化的時候,把必須要做的事做完,
LogcatCatcher()類初始化不需要任何引數輸入,但是需要新建2個屬性,
且必須在初始化的時候把這2個屬性新建好, - 非必要暴露給其他模塊的函式(或屬性),盡量考慮私有化,加_即可,比如_create_hf(self)函式,就是私有函式,
# coding=utf-8
import os
import subprocess
from datetime import datetime
COMMAND = "adb logcat -v threadtime" # 常量
class LogcatCathcher():
def __init__(self):
self.log_file = None
self.hf = None
self._create_hf()
self.p_obj = subprocess.Popen(
args=COMMAND,
stdin=None, stdout=self.hf,
stderr=self.hf, shell=False)
def _create_hf(self):
now_time = datetime.now().strftime("%Y%m%d_%H%M%S")
self.log_file = "Logcat_" + now_time + ".txt"
# 因為沒指定具體路徑,默認就是在當前腳本運行的路徑下創建這個log_file
self.hf = open("%s" % self.log_file, "wb")
def catch_logcat(self):
print("Logcat catching...")
# 持續詢問是否需要停止截取
judgement = input("If you think it is enough, please input Y:")
while (judgement != "Y" and judgement != "y"): ###這里必須是and.
print("Invalid input, please input Y")
judgement = input("If you think it is enough, please input Y:")
else:
if (judgement == "Y" or judgement == "y"): # 如果收到停止Yes信號,則開始結束截取
self.p_obj.terminate()
self.p_obj.kill()
self.hf.close() # 關閉檔案句柄
return os.path.abspath(self.log_file)
if __name__ == '__main__':
l_obj = LogcatCathcher()
print("Logcat log has saved to %s" % l_obj.catch_logcat())
os.system("pause")
代碼運行方式
確保Android車機設備通過USB線與電腦連接了,adb設備有效連接,
以上代碼的3種實作形式都可以直接運行,比如保存為catch_logcat.py并放在桌面,
建議python catch_logcat.py運行,當然也可以雙擊運行,
運行效果如下:
碰到復雜的源代碼,不要著急,慢慢地按以上方法去剖析,
非常有必要查看Python官方的subprocess模塊檔案或源代碼上的注釋檔案;
如果還是不懂,可以看python關于subprocess的原始碼(C:\Users
\Administrator\AppData\Local\Programs\Python\Python37-32\Lib\subprocess.py),
如果想知道別人是怎么呼叫的,可以用findstr或者grep搜下python37-32整個檔案夾下,
有多少源代碼的模塊呼叫了subprocess.Popen,看別人是怎么呼叫的,
如果自己在呼叫的方法上沒問題,但是報錯了,可以去www.bing.com上搜下代碼錯誤的原因,
方法總是比困難多,各位加油!
更多更好的原創文章,請訪問官方網站:www.zipython.com
自拍教程(自動化測驗Python教程,武散人編著)
原文鏈接:https://www.zipython.com/#/detail?id=d6b406f34b4f470a9d95eddc426a273c
也可關注“武散人”微信訂閱號,隨時接受文章推送,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/175003.html
標籤:Python
