You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QProcess如何在同一窗口同一会话中写入执行多条命令

问题原因

你当前的实现每次调用cmd()接口都会新建独立的QProcess实例,启动全新的adb进程执行单条命令,进程运行结束后就自动销毁,会话上下文(包括shell工作目录、权限状态、临时环境变量等)会全部丢失,自然无法实现同一会话下连续执行多条命令的需求。

问题运行截图

实现方案

核心逻辑是长期持有一个常驻的交互型adb shell进程,全程只启动一次,后续所有命令都写入这个进程的标准输入执行,再从标准输出读取结果,不需要每次新建进程。
为了避免多命令输出串读,需要给每条执行的命令追加专属结束标记,读到标记就判定当前命令执行完成。

完整实现代码

首先是Adb类的定义:

#include <QObject>
#include <QProcess>
#include <QDateTime>

class Adb : public QObject
{
    Q_OBJECT
public:
    static Adb& instance() {
        static Adb ins;
        return ins;
    }
    // 初始化常驻adb会话,程序启动时调用一次即可
    bool initSession();
    // 同会话执行命令,传入命令字符串,返回执行输出结果
    QString cmd(const QString &command, int timeoutMs = 3000);
    // 关闭会话释放资源
    void closeSession();

private:
    Adb() = default;
    ~Adb() { closeSession(); }
    QProcess* m_shellProc = nullptr;
    const QString m_endFlag = "__ADB_CMD_DONE__"; // 命令结束标识,不要和正常输出冲突
};

对应实现逻辑:

bool Adb::initSession()
{
    // 已经有运行中的会话直接返回
    if (m_shellProc && m_shellProc->state() == QProcess::Running) {
        return true;
    }

    m_shellProc = new QProcess(this);
    // 启动交互模式的adb shell
    m_shellProc->start("adb shell");
    if (!m_shellProc->waitForStarted(3000)) {
        m_shellProc->deleteLater();
        m_shellProc = nullptr;
        return false;
    }

    // 关闭shell回显,避免输入的命令本身被当成输出读取
    m_shellProc->write("stty -echo\n");
    m_shellProc->waitForBytesWritten(500);
    return true;
}

QString Adb::cmd(const QString &command, int timeoutMs)
{
    // 会话不存在就先初始化
    if (!m_shellProc || m_shellProc->state() != QProcess::Running) {
        if (!initSession()) return "ADB会话启动失败";
    }

    // 写入命令,同时追加echo结束标识的语句,用来判定命令执行终点
    QString execCmd = QString("%1\necho %2\n").arg(command, m_endFlag);
    m_shellProc->write(execCmd.toUtf8());
    m_shellProc->waitForBytesWritten(500);

    QString totalOutput;
    qint64 startTime = QDateTime::currentMSecsSinceEpoch();
    // 循环读取输出,直到读到结束标识或者触发超时
    while (QDateTime::currentMSecsSinceEpoch() - startTime < timeoutMs) {
        if (m_shellProc->waitForReadyRead(100)) {
            // 同时读标准输出和标准错误
            totalOutput += QString::fromUtf8(m_shellProc->readAllStandardError());
            totalOutput += QString::fromUtf8(m_shellProc->readAllStandardOutput());
            if (totalOutput.contains(m_endFlag)) {
                break;
            }
        }
    }

    // 裁掉结束标识和前后多余空白字符
    if (totalOutput.contains(m_endFlag)) {
        totalOutput = totalOutput.left(totalOutput.indexOf(m_endFlag)).trimmed();
    }
    return totalOutput;
}

void Adb::closeSession()
{
    if (m_shellProc) {
        if (m_shellProc->state() == QProcess::Running) {
            m_shellProc->write("exit\n");
            m_shellProc->waitForFinished(1000);
            m_shellProc->kill();
        }
        m_shellProc->deleteLater();
        m_shellProc = nullptr;
    }
}
使用说明
  • 程序启动时调用一次Adb::instance().initSession()初始化会话即可,后续所有命令直接调用cmd()方法,所有操作都在同一个shell会话中执行,cd切换目录、su提权、export设置环境变量等操作的状态都会全程保留
  • 结束标识可以自行修改,只要保证不会和业务命令的正常输出内容冲突即可
  • 如果需要执行logcat这类持续输出的长耗时命令,不要用这个同步接口,改为绑定m_shellProc的readyReadStandardOutput信号做异步读取即可,否则会触发超时逻辑
  • 如果adb连接意外断开,下次调用cmd()时会自动尝试重新建立会话
  • 不要在会话中直接执行exit命令,否则会直接终止常驻shell进程

内容的提问来源于stack exchange,提问作者NOOBY SHARP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 22:15:49