Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
582 views
in Technique[技术] by (71.8m points)

c++ - reading and writing to QProcess in Qt Console Application

Noted: this appears to be a specific issue question but hopefully it can be edited for all to related to

I need to interact with a QProcess object.

The Problem:

I am not getting any output from QProcess after calling QProcess:write(input)

More Info:

Going through the doc pages led me to create an example below:

I have a script requesting user input, and finally displaying and appropriate message based on the user input.

Testing:

After adding a "log" feature to my script for testing, the following occurs:

  • script executes
  • script requests user input (confirmed by the 'first' qDebug() << p->readAll())
  • script accepts input from QProcess (confirmed by script 'log output')

After this, no output is received. The following 2 debug statements both fire (i.e. wait 30s each)

if (!p->waitForReadyRead()) {
    qDebug() << "waitForReadyRead() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
}
if (!p->waitForFinished()) {
    qDebug() << "waitForFinished() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
}

Followed by:

QString s = QString(p->readAll() + p->readAllStandardOutput());

where s is an empty string.

The issue is s should contain either "success" or "failed"

Calling Code:

QString cmd = QString("sh -c "/path/to/bashscript.sh"");
QString input = QString("Name");
QString result = runCommand(cmd, input)

Process Code:

//takes 2 parameters, 
//    cmd which is the code to be executed by the shell
//    input which acts as the user input

QString runCommand(QString cmd, QString input){
    QProcess *p = new QProcess(new QObject());
    p->setProcessChannelMode(QProcess::MergedChannels);   //no actual reason to do this
    p->start(cmd);
    if (p->waitForStarted()) {
        if (!p->waitForReadyRead()) {
            qDebug() << "waitForReadyRead() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
        }
        if (!p->waitForFinished()) {

            //reads current stdout, this will show the input request from the bash script
            //e.g. please enter your name:
            qDebug() << p->readAll();  

            //here I write the input (the name) to the process, which is received by the script
            p->write(ps.toLatin1());

            //the script should then display a message i.e. ("success" o "failed")
            if (!p->waitForReadyRead()) {
                qDebug() << "waitForReadyRead() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
            }
            if (!p->waitForFinished()) {
                qDebug() << "waitForFinished() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
            }
        }
        QString s = QString(p->readAll() + p->readAllStandardOutput());
        return s;
    }
    else{
        qDebug() << "waitForStarted() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
    }
    p->waitForFinished();
    p->kill();
    return QString();
}

script.sh (-rwxrwxr-x)

#!/bin/bash
#returns 
#    "success" on non empty $n value
#    "failed: on empty $n value
#
echo "enter your name:"
read n
if [[ ! -z $n ]];
then
        echo "success"
        exit 0;
else
        echo "failed"
        exit 1;
fi

UPDATE

@KevinKrammer I modified the run command as you said, also using the QStringList with the args.

Still does not get output, infact the waitForReadyRead() and waitForFinished() returns false instantly.

Called with:

QString r = runCommand(QString("text"));

Process Code:

QString runCommand(QString input){      

    QProcess *p = new QProcess(new QObject());    
    p->setProcessChannelMode(QProcess::MergedChannels);

    //script is the same script refered to earlier, and the `cd /home/dev` IS required
    p->start("sh", QStringList() << "-c" << "cd /home/dev" << "./script");
    ;
    if (p->waitForStarted()) {
        if (!p->waitForReadyRead(5000)) {
            qDebug() << "waitForReadyRead() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
        }
        qDebug() << p->readAll();
        p->write(input.toLatin1());
        if(!p->waitForFinished(5000)){
            qDebug() << "waitForFinished() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
        }
        QString s = QString(p->readAll() + p->readAllStandardOutput());
        return s;
    }
    else{
        qDebug() << "waitForStarted() [false] : CODE: " << QVariant(p->error()).toString() << " | ERROR STRING: " << p->errorString();
    }
    p->waitForFinished();
    p->kill();
    return QString();
}

Terminal Output of the Process:

started
readChannelFinished
exit code =  "0"
waitForReadyRead() [false] : CODE:  "5"  | ERROR STRING:  "Unknown error"
""
waitForFinished() [false] : CODE:  "5"  | ERROR STRING:  "Unknown error"
Press <RETURN> to close this window...

Thoughts on this?

UPDATE 2

@Tarod Thank you for taking the time to make a solution.

It works, however not completely is expected.

I copied over your code, exactly.

Made a few changes in the mReadyReadStandardOutput()

See additional info below.

The problem:

After running the application (and script), I get a result -> AWESOME

Everytime it is the incorrect result i.e. "failed". -> NOT AWESOME

Terminal Output:

void MyProcess::myReadyRead()
void MyProcess::myReadyReadStandardOutput()
"enter your name:
"
""
void MyProcess::myReadyRead()
void MyProcess::myReadyReadStandardOutput()
"failed
"
Press <RETURN> to close this window...

script contents:

#!/bin/bash
echo "enter your name:"
read n
echo $n > "/tmp/log_test.txt"
if [[ ! -z "$n" ]];
then
        echo "success"
        exit 0;
else
        echo "failed"
        exit 1;
fi

/tmp/log_test.txt output

myname

running this manually from console:

dev@dev-W55xEU:~$ ls -la script 
-rwxrwxr-x 1 dev dev 155 Jan 25 14:53 script*

dev@dev-W55xEU:~$ ./script 
enter your name:
TEST_NAME
success

dev@dev-W55xEU:~$ cat /tmp/log_test.txt 
TEST_NAME

Full 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 
 (it's like pressing enter key)
    QString s = this->readAllStandardOutput();
    qDebug() << s;
    if (s.contains("enter your name")) {
        this->write(QString("myname" + QString("
")).toLatin1());
        qDebug() << this->readAllStandardOutput();
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyProcess *myProcess = new MyProcess();

    QString program = "/home/dev/script";

    myProcess->start("/bin/sh", QStringList() << program);

    a.exec();
}

#include "main.moc"

script issue? QProcess issue?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Unfortunately I don't have all your code, so I made an example. I hope it helps you.

If I compare my code to yours, I think the problem could be you are not calling readAllStandardOutput() after writing or maybe you are not calling exec() in your main.cpp.

#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 
 (it's like pressing enter key)
    this->write(QString("myname" + QString("
")).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 to test the application:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...