QProcess::readAll and QProcess::readAllStandardOutput both return an empty string after QProcess::write() is run



  • Hi all

    note: this might become confusing, please bare with me!

    To get a basic sense of what I want to do:

    QProcess runs a command by

    QProcess::start("sh -c \"cd /tmp/tempdir ; ./my_script --option file.txt ; echo $?\" ")
    

    The script expects input from the user (a password), QProcess::readAll() confirms the script's input request. The input is given by QProcess::write().

    Here I get lost! -> No output is received. from readAll() and readAllStandardOutput(), both are empty.

    I need the output: particularly the echo $? for later processing, but don't get anything since readAll() and readAllStandardOutput() returns empty strings

    To me there are several possible issues to cause this, maybe more than one:

    • The script does not receive this input
    • The script receives the input, but expects a "return key press"
    • The script receives the input, QProcess::write automatically does a "return key press", the script continues and finishes but QProcess does not read this output

    The Code:

    // please ignore the messiness of the code, a placed a number of debugs just to see the code response.

    cmd = "cd /tmp/tempdir ; ./my_script --option file.txt ; echo $?" (without quotes)
    input => required input for script

    QString gen_serv::runCommand(QString cmd, QString input){
        Process *p = new QProcess(parent);
        p->setProcessChannelMode(QProcess::MergedChannels);
        //    p->start("sh ", QStringList() << " -c " << cmd);
        QString c = QString("sh -c \"" + cmd + "\" ");
        p->start(c);                                 <---- ACTUAL COMMAND : sh -c "cd /tmp/tempdir ; ./my_script --option file.txt ; echo $?"
        if (p->waitForStarted()) {
            if (!p->waitForReadyRead()) {
                qDebug(log_lib_gen_serv) << "waitForReadyRead() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
            }
            if (!p->waitForFinished(1000)) {
                qDebug() << p->readAll();
                qDebug() << p->readAllStandardOutput();
    			//p->write(input.toLatin1());
                p->write(QString(input + QString("\n")).toLatin1());           <--- added this "\n" incase the process/console was waiting for a return press, thus the /n should satisfy the return requirement
                qDebug() << p->readAll();
                qDebug() << p->readAllStandardOutput();
                if (!p->waitForFinished()) {
                    qDebug() << p->readAll();
                    qDebug() << p->readAllStandardOutput();
                    qDebug(log_lib_gen_serv) << "waitForFinished() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
                }
                qDebug() << p->readAll();
                qDebug() << p->readAllStandardOutput();
            }
            QString s = QString(p->readAll() + p->readAllStandardOutput());
            return s;
        }
        else{
            qDebug(log_lib_gen_serv) << "waitForStarted() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
        }
        p->waitForFinished();
        p->kill();
        return QString();
    }
    

    Please note:

    The script is long and complicated, however it returns a message "done" if successfully executed and an exit code for each case.
    Thus the echo $? should capture and return this for processing i.e. return s

    I hope this makes sense, any suggestions how I can attempt to solve this issue?



  • Did u start event loop before waiting for the response to come from your external processes?



  • @dheerendra hi, thanks for the response.

    Event loop? This is all the code related to the process.

    If you could provide an example on a possible event loop, I believe it would be of much use

    Thanks!


  • Moderators

    @CybeX After writing you should first wait for readyRead.
    Instead you read just after writing:

    p->write(QString(input + QString("\n")).toLatin1()); 
    qDebug() << p->readAll();
    qDebug() << p->readAllStandardOutput();
    


  • why, oh why, did The Lord invent the synchronous API?! it's an aberration!

    QString gen_serv::runCommand(const QString& cmd, const QString& input){
    QString result;
    QEventLoop looper;
    Process *p = new QProcess(&looper);
    p->setProcessChannelMode(QProcess::MergedChannels);
    QObject::connect(p,&QProcess::finished,&looper,&QEventLoop::quit);
    QObject::connect(p,&QProcess::errorOccurred,[&result]()->void{qDebug("Error in Process"); result.clear();});
    QObject::connect(p,&QProcess::errorOccurred,&looper,&QEventLoop::quit);
    QObject::connect(p,&QProcess::started,[p,&input]()->void{p->write((input +'\n').toLatin1());});
    QObject::connect(p,&QProcess::readyReadStandardOutput,[p,&result]()->void{result+=p->readAllStandardOutput();});
    const QString c = QString("sh -c \"" + cmd + "\" ");
    p->start(c);
    looper.exec();
    return result;
    }
    


  • @VRonin said in QProcess::readAll and QProcess::readAllStandardOutput both return an empty string after QProcess::write() is run:

    hi

    Thank you very much for the solution!

    Just one small issue:

    error: ‘connect’ was not declared in this scope
         connect(p,&QProcess::finished,&looper,&QEventLoop::quit);
                                                                ^
    

    Not so sure about this one...



  • put QObject:: in front, edited the code above



  • @VRonin hi, same 'same' error

    My includes for what it is worth

    #include <QCoreApplication>
    #include <QLoggingCategory>
    #include <QTextStream>
    #include <QProcess>
    #include <QString>
    #include <QVariant>
    #include <QDebug>
    #include <QObject>
    #include <QEventLoop>
    

    Edited Code as give above:

    QString runCommand(const QString& cmd, const QString& input){
    
        //I ignore cmd for testing purposes. I use a straightup cd to script and execute
    
        QString result;
        QEventLoop looper;
        QProcess *p = new QProcess(&looper);
        p->setProcessChannelMode(QProcess::MergedChannels);
        QObject::connect(p,&QProcess::finished,&looper,&QEventLoop::quit);
        QObject::connect(p,&QProcess::errorOccurred,[&result]()->void{qDebug("Error in Process"); result.clear();});
        QObject::connect(p,&QProcess::errorOccurred,&looper,&QEventLoop::quit);
        QObject::connect(p,&QProcess::started,[p,&input]()->void{p->write((input +'\n').toLatin1());});
        QObject::connect(p,&QProcess::readyReadStandardOutput,[p,&result]()->void{result+=p->readAllStandardOutput();});
        const QString c = QString("sh -c \" cd /home/dev; ./script\" ");
        p->start(c);
        looper.exec();
        return result;
    }
    

    Error:

    main.cpp:296: error: no matching function for call to ‘QObject::connect(QProcess*&, <unresolved overloaded function type>, QEventLoop*, void (QEventLoop::*)())’
         QObject::connect(p,&QProcess::finished,&looper,&QEventLoop::quit);
                                                                         ^
    


  • My bad. if it happens again look at the documentation for the signal and/or slot (depending where <unresolved overloaded function type> appears). In this case: http://doc.qt.io/qt-5/qprocess.html#finished

    so instead of &QProcess::finished use static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished)



  • I would like to post my Stack Oveflow answer. It's actually an example where we write the name and get also the output given by the script.

    Code:

    #include <QCoreApplication>
    #include <QProcess>
    #include <QDebug>
    
    class MyProcess : public QProcess
    {
        Q_OBJECT
    
    public:
        MyProcess(QObject *parent = 0);
        ~MyProcess() {}
    
    public slots:
        void myReadyRead();
        void myReadyReadStandardOutput();
    };
    
    MyProcess::MyProcess(QObject *parent)
    {
        connect(this,SIGNAL(readyRead()),
                this,SLOT(myReadyRead()));
        connect(this,SIGNAL(readyReadStandardOutput()),
                this,SLOT(myReadyReadStandardOutput()));
    }
    
    void MyProcess::myReadyRead() {
        qDebug() << Q_FUNC_INFO;
    }
    
    void MyProcess::myReadyReadStandardOutput() {
        qDebug() << Q_FUNC_INFO;
        // Note we need to add \n (it's like pressing enter key)
        this->write(QString("myname" + QString("\n")).toLatin1());
        // Next line no required
        // qDebug() << this->readAll();
        qDebug() << this->readAllStandardOutput();
    
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        MyProcess *myProcess = new MyProcess();
    
        QString program = "/home/fran/code/myscript.sh";
    
        myProcess->start("/bin/sh", QStringList() << program);
    
        a.exec();
    }
    
    #include "main.moc"
    

    Script:

    echo "enter your name:"
    read n
    if [ ! -z "$n" ];
    then
            echo "success"
            exit 0;
    else
            echo "failed"
            exit 1;
    fi
    




Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.