在 Qt 控制台应用程序中读写 QProcess

Posted

技术标签:

【中文标题】在 Qt 控制台应用程序中读写 QProcess【英文标题】:reading and writing to QProcess in Qt Console Application 【发布时间】:2017-01-25 10:18:31 【问题描述】:

注意:这似乎是一个特定的问题,但希望它可以为所有相关的人编辑

我需要与 QProcess 对象交互。

问题:

在调用QProcess:write(input) 后,我没有从QProcess 得到任何输出

更多信息:

通过doc pages 引导我创建了以下示例:

我有一个脚本请求用户输入,最后根据用户输入显示和适当的消息。

测试:

在我的脚本中添加“日志”功能进行测试后,会发生以下情况:

脚本执行 脚本请求用户输入(由“第一”qDebug() << p->readAll() 确认) 脚本接受来自QProcess 的输入(由脚本“日志输出”确认)

在此之后,没有收到任何输出。以下 2 个调试语句都触发(即每个等待 30 秒)

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());

其中s 是一个空字符串。

问题是s 应该包含“成功”或“失败”

调用代码:

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

流程代码:

//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

更新

@KevinKrammer 我按照你说的修改了运行命令,也使用了带参数的 QStringList。

仍然没有得到输出,事实上waitForReadyRead()waitForFinished() returns false 立即。

调用:

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

流程代码:

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();

进程的终端输出:

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...

对此有什么想法?

更新 2

@Tarod 感谢您抽出宝贵时间提出解决方案。

它可以工作,但并不完全是预期的。

我完全复制了你的代码。

mReadyReadStandardOutput()做了一些改动

请参阅下面的其他信息。

问题:

运行应用程序(和脚本)后,我得到了一个结果 -> AWESOME

每次都是不正确的结果,即“失败”。 -> 不是太棒了

终端输出:

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

脚本内容:

#!/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 输出

myname

从控制台手动运行:

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

完整代码:

#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)
    QString s = this->readAllStandardOutput();
    qDebug() << s;
    if (s.contains("enter your name")) 
        this->write(QString("myname" + QString("\n")).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"

脚本问题? QProcess 问题?

【问题讨论】:

为什么在写信之前要打电话给waitForFinished?给定您的程序,顺序是等待启动,然后循环等待读取,直到您收到预期的字符串,写入,然后等待完成。对于命令,我还建议分别传递命令和参数,即命令作为start() 的第一个参数,并将其所有参数作为第二个参数,因为这也将正确自动处理转义。 @KevinKrammer 谢谢你的建议,我会应用它并返回结果 @KevinKrammer 那是不行的。请看更新 如果你只执行sh而不是你的脚本会发生什么? /bin/sh 呢? waitForFinished 返回false 如果该过程已经完成。未知错误只是error() 的默认值。我认为这是一个糟糕的 API 决定,但至少可以解释为什么你会在那里得到false 【参考方案1】:

很遗憾我没有你所有的代码,所以我做了一个例子。希望对你有帮助。

如果我将我的代码与您的代码进行比较,我认为问题可能是您在编写后没有调用 readAllStandardOutput(),或者您可能没有在 main.cpp 中调用 exec()

#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"

测试应用程序的脚本:

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

【讨论】:

根据文档QProcess 也应该在没有事件循环的情况下工作,因此不需要app.exec() 好的。无论如何,由于信号/插槽方法,在我的示例中是必需的。谢谢你,@georgschölly @Tarod 我一直在寻找信号槽方法,在forum.qt.io 上建议使用Eventloop,虽然我现在觉得使用信号槽方法更舒服。我将对此进行测试,我真的希望它有效:p @Torad 请参阅更新 2,我想我/我们正在缩小这个问题的范围...... @KGCybeX 不幸的是,我不知道正确的代码。也许是脚本中的if 语句。请改用if [ ! -z "$n" ];

以上是关于在 Qt 控制台应用程序中读写 QProcess的主要内容,如果未能解决你的问题,请参考以下文章

使用 QProcess 为 c++ 控制台应用程序实现前端

Qt:无法使用 QProcess 启动 Windows 控制台

带有 CreateNoWindow 的 QProcess

Qt,ProcessState 枚举如何工作

试图从生成的 QProcess 中读取标准输出

QProcess 无法执行简单的控制台程序