输出截断返回值 141 - 为什么大输出命令会被提前终止
返回值 141 是什么
shell 命令执行完后 Runner 会显示一个"返回值"字段——正常成功是 0,失败是非 0。其中 141 是 Runner 特有的信号:
- 141 =
SIGPIPE(Broken Pipe)——shell 子进程在写入输出时,发现接收端(Runner)已经关闭了管道
这不是命令本身的错误,也不是 Runner 的 bug——是 Runner 主动做的。
为什么要截断
Runner 的实现机制里,它是通过 Android Binder 通道读取 shell 命令的 stdout/stderr 输出。但 Binder 通道有单次传输大小限制,而且如果 shell 命令一直在持续输出,Runner 界面会被撑爆。
所以 Runner 做了一个安全设计:单次命令输出累计约 1000 字符后,关闭输出管道,终止子进程。
哪些命令容易被截断
| 命令 | 预期输出量 | 是否容易截断 |
|---|---|---|
echo hello | 几字节 | 否 |
dumpsys battery | 几十行 | 可能 |
dumpsys activity all | 几百行 | 一定 |
pm list packages | 几十到几百行 | 可能(取决于装了多少 App) |
logcat -d | 可能几千行 | 一定 |
echo $(getprop ro.build.*) | 几十行 | 可能 |
怎么处理截断
方法一:重定向到文件
让 shell 子进程直接写文件,不走 Runner 的输出通道:
# Runner 里执行这条命令
dumpsys activity all > /sdcard/activity_dump.txt 2>&1
命令本身正常执行、正常结束,只是输出没传给 Runner 显示——这样 Runner 不会截断,文件里能拿到完整内容。之后你可以:
- 用文件管理器打开
/sdcard/activity_dump.txt - 用电脑 adb pull 出来看
- 或者 Runner 里
cat /sdcard/activity_dump.txt(如果文件本身也不太大)
方法二:分页/过滤
用 grep、head、tail 过滤出你真正关心的部分:
# 只看进程名和 PID
dumpsys activity activities | grep "mResumedActivity"
# 只看最后 50 行
logcat -d | tail -50
# 只查某个 App 的 package info
dumpsys package com.example.app | head -100
方法三:分块执行
如果必须看全部内容,把大命令拆成小块:
# 不要一次跑完
dumpsys package
# 改为按包名逐个查
dumpsys package com.tencent.mm
dumpsys package com.sina.weibo
判断是截断还是命令本身失败
看到返回值非 0 时怎么判断:
| 返回值 | 含义 | 怎么判断 |
|---|---|---|
| 0 | 成功 | 输出正常显示 |
| 非 0 非 141 | 命令本身报错 | 看输出内容里的错误信息 |
| 141 | Runner 主动截断 | 输出内容到一半突然断了,没尾部 |
141 的特征很明显——输出内容没有结尾,中间突然停住。比如 dumpsys package 的输出会在某个 service 的配置中间断掉,没有正常的 Package [name=...] 结尾。
小结
不要把 Runner 当完整的终端看。Runner 的输出通道是有限的。大输出要么重定向到文件,要么过滤分页。
如果你需要长期稳定执行大输出脚本,建议:
- 脚本写好推到
/data/local/tmp/ - 脚本里自己处理输出(写文件、过滤等)
- Runner 只负责"启动脚本"这一步,不管脚本内部的输出管理