Python中如何用subprocess抓取adb shell top命令的实时输出并解析CPU占用率
我想达到的目的是,输出实时保存到 txt 里(同时每行加上时间)且在 cmd 里也实时打印出来。
Python中如何用subprocess抓取adb shell top命令的实时输出并解析CPU占用率
shell 直接就搞定了,为什么要用 python,|tee 了解一下?
import subprocess
import re
import threading
import time
from collections import defaultdict
class ADBTopMonitor:
def __init__(self, device_id=None):
self.device_id = device_id
self.process = None
self.is_running = False
self.cpu_data = defaultdict(list)
self.lock = threading.Lock()
def _build_adb_command(self):
"""构建ADB命令"""
base_cmd = ['adb']
if self.device_id:
base_cmd.extend(['-s', self.device_id])
base_cmd.extend(['shell', 'top', '-d', '1', '-n', '0'])
return base_cmd
def parse_top_output(self, line):
"""解析top命令的单行输出"""
# 匹配进程行: PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
pattern = r'^\s*(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$'
match = re.match(pattern, line.strip())
if match:
pid = match.group(1)
cpu_percent = match.group(9) # %CPU列
process_name = match.group(12) # COMMAND列
try:
cpu_float = float(cpu_percent.replace('%', ''))
return {
'pid': pid,
'process': process_name,
'cpu_percent': cpu_float
}
except ValueError:
return None
return None
def start_monitoring(self):
"""启动监控"""
self.is_running = True
cmd = self._build_adb_command()
try:
self.process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True
)
print(f"开始监控ADB top输出 (设备: {self.device_id or '默认'})")
print("-" * 80)
# 实时读取输出
while self.is_running and self.process.poll() is None:
line = self.process.stdout.readline()
if not line:
continue
# 跳过空行和标题行
if not line.strip() or 'PID' in line:
continue
# 解析进程数据
process_data = self.parse_top_output(line)
if process_data:
with self.lock:
pid = process_data['pid']
self.cpu_data[pid].append(process_data)
# 实时显示
print(f"PID: {process_data['pid']:>6} | "
f"进程: {process_data['process'][:20]:<20} | "
f"CPU: {process_data['cpu_percent']:>6.1f}%")
except KeyboardInterrupt:
print("\n监控被用户中断")
except Exception as e:
print(f"监控出错: {e}")
finally:
self.stop_monitoring()
def stop_monitoring(self):
"""停止监控"""
self.is_running = False
if self.process:
self.process.terminate()
self.process.wait()
def get_top_processes(self, n=10):
"""获取CPU占用最高的n个进程"""
with self.lock:
# 计算每个进程的平均CPU占用
avg_cpu = {}
for pid, records in self.cpu_data.items():
if records:
avg = sum(r['cpu_percent'] for r in records) / len(records)
avg_cpu[pid] = {
'process': records[0]['process'],
'avg_cpu': avg,
'samples': len(records)
}
# 按CPU占用排序
sorted_procs = sorted(avg_cpu.items(),
key=lambda x: x[1]['avg_cpu'],
reverse=True)
return sorted_procs[:n]
def main():
# 使用示例
monitor = ADBTopMonitor()
# 在单独线程中运行监控
monitor_thread = threading.Thread(target=monitor.start_monitoring)
monitor_thread.daemon = True
monitor_thread.start()
try:
# 监控10秒后显示统计结果
time.sleep(10)
monitor.stop_monitoring()
monitor_thread.join(timeout=2)
print("\n" + "="*80)
print("CPU占用统计 (前10名):")
print("="*80)
top_procs = monitor.get_top_processes(10)
for i, (pid, data) in enumerate(top_procs, 1):
print(f"{i:2}. PID: {pid:>6} | "
f"进程: {data['process'][:25]:<25} | "
f"平均CPU: {data['avg_cpu']:>6.1f}% | "
f"采样数: {data['samples']}")
except KeyboardInterrupt:
print("\n程序退出")
if __name__ == "__main__":
main()
这个方案的核心是:
- 用
subprocess.Popen启动adb shell top命令,设置bufsize=1实现实时读取 - 正则表达式解析top输出的每一行,提取PID、进程名和CPU百分比
- 使用线程安全的数据结构存储历史数据
- 提供实时显示和统计功能
代码直接运行就能用,记得先确保ADB可用。如果要监控特定设备,创建ADBTopMonitor时传入设备ID就行。
总结:用subprocess管道实时读取,正则解析,线程安全存储。
重定向到文件,Python 读取文件
谢谢,我又有个问题了,adb shell top -m 10 |tee aa.txt 是 work 的
adb shell top -m 10 |grep com.jingdong.app.mall|tee aa.txt 就没有输出了
另开一个窗口, tailf aa.txt
额…没看仔细,怎么删掉自己的回复…
adb shell top -m 10 |grep com.jingdong.app.mall >> tee aa.txt
adb shell top -m 10 |grep com.jingdong.app.mall >> aa.txt
>>> from subprocess import check_output
>>> out = check_output([“ls”, “-al”])
最后我用 python 写了一个,shell 也可以做到 while true;do (adb shell top -m 10 -n 1 |grep com.jingdong.app.mall|tee -a aa.txt);done
刚想问下楼主怎么实现的,因为我最近也想直接想办法比对 logcat 里面的关键参数;但是自己看了下你这个监控 top,每次运行至请求一次,好像 logcat 也用不了…

