Android作爲一款Linux終端,肯定是支持.sh後綴的Shell腳本的運行的,
有時候測試環境準備或者長時間截取複雜的日誌等,開發會給到一些Shell腳本。
Shell腳本的執行的優勢:
- 快捷高效,Shell腳本是Linux終端都支持的。
- 由於執行及測試結果都在Linux終端內部存儲,不會出現因爲反覆通過USB與Windows電腦進行輸入輸出導致的Android系統的I/O CPU消耗過大。
如何通過Python來運行Shell腳本呢?
何爲高端地用Python運行Shell腳本,這裏邊的學問可不小,
以下案例,我們寫了一個top.sh腳本, 用於持續截取系統各App的cpu佔用率情況。
持續截取cpu佔用率數據,也是App性能測試(資源消耗)的常用測試方法。
Shell腳本的三種運行方式
具體命令 | 具體效果 |
---|---|
adb shell sh top.sh (低端) | 執行在前臺,即“阻塞”在前臺運行,會讓你在這個界面等着。 拔掉USB線後,Shell腳本自動停止執行 |
adb shell sh top.sh &(中端) | 執行在後臺,即“不阻塞”,在後臺執行了,不需要你等着執行完。 拔掉USB線後,Shell腳本自動停止執行 |
adb shell nohup sh top.sh &(高端) | 獨立執行在後臺,執行後,即使你拔掉USB線,Shell腳本依舊後臺運行。 |
本案例用第三種高端方式來實現
Shell腳本與Python文件相互存在的兩種方式
融合方式 | 具體效果 |
---|---|
顯露式(低端) | top.sh腳本和Python腳本都是獨立的文件,top.sh顯露在外 —run_top_sh.py —top.sh |
隱藏式(高端) | 將top.sh以文本的形式隱藏在Python代碼中 —run_top_sh.py |
顯露式的方法,肯定是adb push top.sh /sdcard/top.sh,
本案例腳本用第二種隱藏式高端方式來實現, 具體如何實現呢?
準備階段
第1步: 將Shell腳本以字符串變量的形式存放於Python代碼塊裏
第2步: 將Shell腳本寫入一個臨時文件(注意Shell腳本是需要運行在Linux,Linux的行尾符是\n)
第3步: 將以上臨時文件adb push 到/sdcard/cpu.sh
第4步: 用adb shell nohup sh /sdcard/cpu.sh & 的方式實現長時間截取,即使USB不小心掉了,也不影響Shell腳本繼續在後執行截取top。
Python批處理腳本形式
記住批處理腳本的精髓:批量順序執行語句
# coding=utf-8
import os
import tempfile
# 將top.sh的Shell腳本copy過來,作爲字符串變量
top_sh = '''#!/bin/sh
while true
do
top -n 1 >> /sdcard/top.log
echo -e "$(date +%Y-%m-%d_%H:%M:%S)" >> /sdcard/top.log
sleep 3
done
'''
print("正在生成Shell腳本的臨時文件......")
signal, temp_file = tempfile.mkstemp() # 創建一個臨時文件
with open(temp_file, 'w+', newline="\n") as hf: # 將支付按此轉成Shell腳本,重點注意換行符"\n"
for line in top_sh:
hf.write(line)
os.system("adb root") #必要的root
os.system("adb remount")
os.system("adb wait-for-device")
os.system("adb push %s /sdcard/top.sh" % temp_file) # 推top.sh腳本到終端設備
os.system("adb shell chmod 777 /sdcard/top.sh") # 賦值777
os.popen("adb shell nohup sh /sdcard/top.sh &") # 獨立後臺無干擾執行,popen不阻塞
print("/sdcard/top.sh 腳本後臺無干擾運行中......")
print("清除臨時文件......")
os.close(signal) # 臨時文件清理
os.remove(temp_file) # 臨時文件清理
os.system("pause")
Python面向過程函數形式
面向過程函數的編程思維應該是這樣的:
你需要多少個功能(函數),才能做成這個事。
最好把功能(函數)都儘量封裝好,只暴露一定的參數接口即可。
import os
import tempfile
# 將top.sh的Shell腳本copy過來,作爲字符串變量
top_sh = '''#!/bin/sh
while true
do
top -n 1 >> /sdcard/top.log
echo -e "$(date +%Y-%m-%d_%H:%M:%S)" >> /sdcard/top.log
sleep 3
done
'''
def generate_shell(shell_script):
print("正在生成Shell腳本的臨時文件......")
signal, temp_file = tempfile.mkstemp() # 創建一個臨時文件
with open(temp_file, 'w+', newline="\n") as hf: # 將支付按此轉成Shell腳本,重點注意換行符"\n"
for line in shell_script:
hf.write(line)
return signal, temp_file
def clear_tempfile(signal, temp_file):
print("清除臨時文件......")
os.close(signal)
os.remove(temp_file)
def push_run_shell(sh_file, push_path):
os.system("adb root") #必要的root
os.system("adb remount")
os.system("adb wait-for-device")
os.system("adb push %s %s" % (sh_file, push_path)) # 推top.sh腳本到終端設備
os.system("adb shell chmod 777 %s" % push_path) # 賦值777
os.popen("adb shell nohup sh %s &" % push_path) # 獨立後臺無干擾執行,popen不阻塞
print("%s 腳本後臺無干擾運行中......"%push_path)
signal, temp_file = generate_shell(top_sh)
push_run_shell(temp_file, "/sdcard/top.sh")
clear_tempfile(signal, temp_file)
os.system("pause")
Python面向對象類形式
面向對象類的編程思維應該是這樣的:
如果給你一個空白的世界,在這個世界裏你需要哪些種類的事物,
這些種類的事物都具備哪些共有的屬性與方法,
這些種類(類)的事物(對象),和其他種類(其他類)的事物(其他對象)有什麼關係。
儘量把這些類封裝好,只暴露對外的屬性(變量)和方法(函數)即可。
# coding=utf-8
import os
import tempfile
# 將top.sh的Shell腳本copy過來,作爲字符串變量
top_sh = '''#!/bin/sh
while true
do
top -n 1 >> /sdcard/top.log
echo -e "$(date +%Y-%m-%d_%H:%M:%S)" >> /sdcard/top.log
sleep 3
done
'''
class ShellGeneratorAndRuner():
'''Generate shell and push into android and run it'''
def __init__(self, shell_script, push_path):
self.script_text = shell_script
self.push_path = push_path
self.signal = None
self.temp_file = None
def generate_shell(self):
print("正在生產Shell腳本的臨時文件......")
self.signal, self.temp_file = tempfile.mkstemp()
with open(self.temp_file, 'w+', newline="\n") as hf: # 將支付按此轉成Shell腳本,重點注意換行符"\n"
for line in self.temp_file:
hf.write(line)
def push_run_shell(self):
os.system("adb root") #必要的root
os.system("adb remount")
os.system("adb wait-for-device")
os.system("adb push %s %s" % (self.temp_file, self.push_path)) # 推top.sh腳本到終端設備
os.system("adb shell chmod 777 %s" % self.push_path) # 賦值777
os.popen("adb shell nohup sh %s &" % self.push_path) # 獨立後臺無干擾執行,popen不阻塞
print("%s 腳本後臺無干擾運行中......"%self.push_path)
def clear_tempfile(self):
os.close(self.signal)
os.remove(self.temp_file)
if __name__ == '__main__':
s_obj = ShellGeneratorAndRuner(top_sh, "/sdcard/top.sh")
s_obj.generate_shell()
s_obj.push_run_shell()
s_obj.clear_tempfile()
os.system("pause")
運行方式與效果
確保Android設備通過USB線與電腦連接了,adb設備有效連接,
以上代碼的3種實現形式都可以直接運行,比如保存爲run_top_sh.py並放在桌面,
建議python run_top_sh.py運行,當然也可以雙擊運行。
運行效果如下:
其中C:\Users\ADMINI~1\AppData\Local\Temp\tmp5way7qgx這個就是生成的臨時文件,
由於一般用戶不會涉及到以上臨時文件,所以可以實現“無感”地生成Shell腳本。
爲什麼鼓勵用隱藏式來隱藏Shell腳本到代碼裏去,
因爲比如後續你寫了一個Python工具,這個工具用py2exe編譯時,
py2exe只能編譯打包.py的文件成.exe, 其他的任何非.py的文件無法打包進來,
如果你發佈給別人用的時候,也就一個.exe,
大家就覺得你的工具做的比較好,集成的比較好。
相反地,如果你的.exe工具再帶一堆的Shell腳本,或者其他資源文件,配置文件等,
則相對而言沒那麼易用,比如容易動不動出現配套文件找不到,
或者被用戶隨意篡改導致程序無法正常運行,也無法顯示你的作品的牛逼。。。
不僅僅是Shell腳本,任何文本形式的文件(配置文件,腳本文件,其他log文件等等),
都可以考慮用以上這種方法附帶。。。
更多更好的原創文章,請訪問官方網站:www.zipython.com
自拍教程(自動化測試Python教程,武散人編著)
原文鏈接:https://www.zipython.com/#/detail?id=97cbb5fd8dcf4c40a549e565ca0b6f6a
也可關注“武散人”微信訂閱號,隨時接受文章推送。