确定QProcess是否已完成执行或正在等待用户输入(或响应)

时间:2017-01-25 23:01:19

标签: c++ bash qt shell qprocess

我有2个bash脚本

  1. 一个不需要用户输入的脚本,它显示信息
  2. 另一个需要一些用户输入,并显示一些信息
  3. 问题:

    创建QProcess时,如果QProcess已完成或暂停用户输入,我无法区分。

    我希望有一些功能,例如QProcess::atEnd如果脚本完成则返回true,但即使QProcess挂起用户输入(来自脚本),它也返回true

    更多信息:

    • 需要用户输入的脚本:name-please.sh

    • 不需要输入的脚本:hello.sh

    name-please.sh

    #!/bin/bash
    echo "I am Mr Robot"
    echo "what is your name:"
    
    read n
    
    if [[ ! -z "$n" ]];
    then
            echo "please to meet you"
            exit 0;
    else
            echo "ok, bye..."
            exit 1;
    fi
    

    hello.sh

    #!/bin/bash
    echo "I am Mr Robot"
    

    使用mReadyReadStandardOutput(),我读了脚本输出。问题在于我需要读取输出,确定输出是否包含请求用户信息的行(来自脚本的行)并对其作出响应。

    查看带指针的代码

    代码:

     #include <QCoreApplication>
    #include <QLoggingCategory>
    #include <QTextStream>
    #include <QProcess>
    #include <QString>
    #include <QVariant>
    #include <QDebug>
    #include <QObject>
    #include <QEventLoop>
    
    class m_Proc : public QProcess
    {
        Q_OBJECT
    
    public:
        int exitCode = -1;
        QString _out, _input;
    
        m_Proc(QString input = QString(), QObject *parent = 0);
        ~m_Proc() {}
    
    public slots:
        void myReadyReadStandardOutput();
        void myFinished(int i);
    };
    
    m_Proc::m_Proc(QString input, QObject *parent)
    {
        connect(this,SIGNAL(readyReadStandardOutput()),
                this,SLOT(myReadyReadStandardOutput()));
        connect(this, SIGNAL(finished(int)),
                this, SLOT(myFinished(int)));
    }
    
    void m_Proc::myReadyReadStandardOutput() {
        // Note we need to add \n (it's like pressing enter key)
        _out = this->readAllStandardOutput();
        if (!_input.isEmpty()) {
    
            /* 
             * check if line matches that of the script, 
             * where the user is required to enter their name
             */
    
            if (_out.contains("what is your name")) {         <<< ---- LINE WITH ISSUE
                this->write(QString(_input + QString("\n")).toLatin1());
                _out = this->readAllStandardOutput();
            }
        }
        else
            qDebug() << "Input Emptry (should not be reached!) !";
    }
    
    void m_Proc::myFinished(int i){
        exitCode = i;
        qDebug() << "exit code = " << QString::number(exitCode);
        qDebug() << _out;
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        m_Proc *myProcess1 = new m_Proc(QString("text"));
    
        //    QString program = "/home/dev/script";
    
        myProcess1->start("/home/dev/script", QStringList());// << "one" << "two");
    
        //    myProcess1->start("/bin/sh", QStringList() << "-c" << "ls" << "-la");
    
    
        a.exec();
    }
    
    #include "main.moc"
    

    有哪些建议绕过这个问题?

1 个答案:

答案 0 :(得分:1)

三件事。

  1. 根据QProcess::atEnd()的文档,仅当进程未运行(QProcess::NotRunning)时才会返回true。那么......当你致电atEnd()时,确定这个过程是不是真的完成了?我在你发布的代码中没有看到它,所以我不知道。

  2. 每次有新数据时,您的myReadyReadStandardOutput()信号都会覆盖m_Proc::_out成员变量。根据您的系统(以及Qt)缓冲对标准输出的写入的方式,您可能会将部分行读入_out。因此,_out.contains()调用可能会返回false,因为您在行中有at is your之类的内容,而不是完整的what is your name:

  3. 将成员字符串_input写入进程的标准输入,然后立即尝试从标准输出中再次读取。这将很可能不会返回_out中您尚未拥有的任何数据。当控制返回到主事件循环时,Qt只与底层设备实际进行实际I / O操作。来自QIODevice的文档:

  4.   

    QIODevice的某些子类,,如QTcpSocket和QProcess ,是异步的。这意味着诸如write()或read()之类的I / O函数总是立即返回,而当控制返回到事件循环时,可能会发生与设备本身的通信。

    我解决您问题的建议是修改myReadyReadStandardOutput信号。对于异步设备(例如套接字和子进程),数据可能以任意大小的块到达。这是我上面提到的 2 点。处理此问题的常用方法是在信号顶部放置如下内容:

    if (!canReadLine())
        return;
    

    如果没有完整的数据线,那么你就会退出信号并返回主事件循环。如果返回true,则可以执行以下操作:

    _out = readLine();
    /* Process an entire line, e.g., check if it matches the request for name */
    

    因此,信号设计为仅在整行数据上运行。这对于面向线路的终端或网络协议非常有用。在这种情况下,您可以调用QString::contains()方法来检查这是否是请求名称的行,如果是,请编写它。但是你必须返回到事件循环才能写入数据,所以你要在下一次调用信号时处理下一行(please to meet you)。全线可用。