【问题标题】:QProcess problems, output of processQProcess问题,流程的输出
【发布时间】:2011-05-04 23:16:17
【问题描述】:

我正在尝试弄清楚 QProcess 的用途。我查看了 Qt 文档,没有运气。
http://doc.qt.io/qt-4.8/qprocess.html

问题示例。

示例 1:以下代码有效。

    #include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QProcess>    

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

    QProcess cmd;
    
    cmd.start("cmd");
    if (!cmd.waitForStarted())  {
        return false;
    }

    cmd.waitForReadyRead();
    QByteArray result = cmd.readAll();
    //qout << result.data() << endl;   //console junk captured, doesn't show. 

    //My test command
    cmd.write("echo hello");
    cmd.write("\n");

    //Capture my result
    cmd.waitForReadyRead();
    //This is my command shown by cmd, I don't show it, capture & discard it.
    result = cmd.readLine();
    //Read result of my command ("hello") and the rest of output like cur dir.   
    result = cmd.readAll();    
    qout << result.data();

    qout << "\n\n---End, bye----" << endl;
    return a.exec();
}

上述代码的输出

你好

F:\Dev_Qt\expControllingExtConsoleApps-build-desktop>

---End, bye----

问题是,如果我尝试通过 Qprocess 和 cmd 控制台以这种方式使用 ipconfig 或 7zip,我将无法看到 ipconfig 或 7zip 的任何输出。我什至不知道是否完成了某些事情,如果完成了某些事情,那为什么我看不到输出?下面的代码说明了。

示例 2: 不起作用。无法使用 ipconfig。

#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QString>
#include <QProcess>    

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   QTextStream qout(stdout);
   
   QProcess cmd2;
    cmd2.setWorkingDirectory("C:/Program Files/7-Zip");   //not needed in this example.
    cmd2.setReadChannel(QProcess::StandardOutput);
    cmd2.setProcessChannelMode(QProcess::MergedChannels);

    cmd2.start("cmd");
    if (!cmd2.waitForStarted())
    {
        qout << "Error: Could not start!" << endl;
        return false;
    }

    cmd2.waitForReadyRead();
    QByteArray result = cmd2.readAll();
    qout << result.data() << endl;      //Console version info, etc.

    //My command
    cmd2.write("ipconfig");
    cmd2.write("\n");

    //Capture output of ipconfig command
    //DOES NOT WORK!!
    cmd2.waitForReadyRead();
    while (! cmd2.atEnd())
    {
        result = cmd2.readLine();
        qout << result;
        result.clear();
    }
    qout << endl;

    qout << "\n\n---end----" << endl;
    return a.exec();

}

输出如下,缺少ipconfig连接信息结果。根本没有捕获 ipconfig 的输出。

Microsoft Windows XP [版本 5.1.2600] (C) 版权所有 1985-2001 Microsoft Corp.

C:\Program Files\7-Zip> ipconfig

---结束----

应该更像这样(带有 ipconfig 结果)。

Microsoft Windows XP [版本 5.1.2600] (C) 版权所有 1985-2001 Microsoft Corp.

C:\文档和 设置\noname>ipconfig

Windows IP 配置

以太网适配器本地 连接:

    Connection-specific DNS Suffix  . :
    IP Address. . . . . . . . . . . . : 192.172.148.135
    Subnet Mask . . . . . . . . . . . : 255.255.255.0
    Default Gateway . . . . . . . . . : 192.172.148.177

C:\Documents and Settings\noname>

显然输出应该比上面有一点差异,但是应该已经捕获了作为“ipconfig”输出的连接信息。同样,如果我尝试通过 cmd 控制台使用 7zip……我看不到/捕获 7zip 的任何输出。所以我的问题是如何通过 QProcess 和 cmd 控制台使用 ipconfig 和 7zip 等命令行应用程序并查看这些应用程序的输出结果?

示例 3: 7zip 不起作用

#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QProcess>    

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

    QProcess cmd2;
    cmd2.setWorkingDirectory("C:/Program Files/7-Zip");
    cmd2.setReadChannel(QProcess::StandardOutput);
    cmd2.setProcessChannelMode(QProcess::MergedChannels);

    cmd2.start("cmd");
    if (!cmd2.waitForStarted()) {
        return false;
    }

    //My Command
    cmd2.write("7z.exe");
    cmd2.write("\n");

    //Capture output of ipconfig command
    cmd2.waitForReadyRead();
    QByteArray result;

    while (! cmd2.atEnd()) {
        result = cmd2.readLine();
        qout << result;
        result.clear();
    }
    qout << endl;

    qout << "\n\n---end----" << endl;
    return a.exec();
}

输出。不显示 7zip 中的任何内容。

Microsoft Windows XP [版本 5.1.2600] (C) 版权所有 1985-2001 Microsoft Corp.

C:\Program Files\7-Zip>7z.exe

---结束----

预计输出将遵循...

Microsoft Windows XP [版本 5.1.2600] (C) 版权所有 1985-2001 Microsoft Corp.

C:\Documents and Settings\noname>cd C:\Program Files\7-Zip

C:\Program Files\7-Zip>7z.exe

7-Zip 9.15 beta 版权所有 (c) 1999-2010 伊戈尔·巴甫洛夫 2010-06-20

用法:7z [...] [...] []

a:将文件添加到存档
b:基准 d:删除文件 归档 e: 从中提取文件 存档(不使用目录 names) l: 列出档案的内容
t:测试存档的完整性 u: 将文件更新到存档 x:eXtract 具有完整路径的文件
-ai[r[-|0]]{@listfile|!wildcard}:包含档案
-ax[r[-|0]]{@listfile|!wildcard}:排除档案 -bd:禁用 百分比指标
-i[r[-|0]]{@listfile|!wildcard}:包含文件名 -m{Parameters}: 设置压缩方法
-o{Directory}:设置输出目录 -p{Password}:设置密码 -r[-|0]:递归子目录 -scs{UTF-8 | 赢 | DOS}:为列表文件设置字符集 -sfx[{name}]:创建 SFX 存档 -si[{name}]:从标准输入读取数据 -slt:显示 l(列表)命令的技术信息 -so:将数据写入 stdout -ssc[-]:设置敏感大小写 模式 -ssw:压缩共享文件
-t{Type}:设置存档类型 -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]: 更新选项 -v{Size}[b|k|m|g]: 创建卷 -w[{path}]: 分配 工作目录。空路径意味着 临时目录
-x[r[-|0]]]{@listfile|!wildcard}:排除文件名 -y:假设是 所有查询

C:\Program Files\7-Zip>

【问题讨论】:

    标签: qt qt4


    【解决方案1】:

    我看到了一个大问题。 在 Windows 下,您可以按 Enter 键发出命令。写作

    cmd.write("command");
    cmd.write("\n");
    


    你必须写是不够的

    cmd.write("command");
    cmd.write("\n\r");
    

    注意尾随的 \r。试试这个,它应该会更好,我的意思是 7zip。我不知道你是否能让 ipconfig 正常工作。

    祝你好运和最好的问候
    D

    编辑 这是一个可行的解决方案:

    
    #include <QtCore/QCoreApplication>
    #include <QtCore/QProcess>
    #include <QtCore/QString>
    #include <QtCore/QTextStream>
    
    // Not clean, but fast
    QProcess *g_process = NULL;
    
    // Needed as a signal catcher
    class ProcOut : public QObject
    {
      Q_OBJECT
    public:
      ProcOut (QObject *parent = NULL);
      virtual ~ProcOut() {};
    
    public slots:
      void readyRead();
      void finished();
    };
    
    ProcOut::ProcOut (QObject *parent /* = NULL */):
    QObject(parent)
    {}
    
    void
    ProcOut::readyRead()
    {
      if (!g_process)
        return;
    
      QTextStream out(stdout);
      out << g_process->readAllStandardOutput() << endl;
    }
    
    void
    ProcOut::finished()
    {
      QCoreApplication::exit (0);
    }
    
    int main (int argc, char **argv)
    {
      QCoreApplication *app = new QCoreApplication (argc, argv);
    
      ProcOut *procOut = new ProcOut();
      g_process        = new QProcess();
    
      QObject::connect (g_process, SIGNAL(readyReadStandardOutput()),
        procOut, SLOT(readyRead()));
      QObject::connect (g_process, SIGNAL(finished (int, QProcess::ExitStatus)),
        procOut, SLOT(finished()));
    
      g_process->start (QLatin1String ("cmd"));
      g_process->waitForStarted();
    
      g_process->write ("ipconfig\n\r");
    
      // Or cmd won't quit
      g_process->write ("exit\n\r");
    
      int result = app->exec();
    
      // Allright, process finished.
      delete procOut;
      procOut = NULL;
    
      delete g_process;
      g_process = NULL;
    
      delete app;
      app = NULL;
    
      // Lets us see the results
      system ("pause");
    
      return result;
    }
    
    #include "main.moc"
    

    希望对您有所帮助。它每次都在我的机器上工作。

    【讨论】:

    • 谢谢,但似乎没有什么不同。不过,我已经意识到一件事-> 使用 AND 而没有 \r ...它有时确实有效。如果我运行该应用程序 10 次,可能 50% 的时间它可以工作,而 50% 的时间它不能。复制提示:在重新运行应用程序之间等待不同的时间。
    • 我不能告诉你确切的原因,但我尝试了不同的方法。没有使用waitForReadyRead(),而是连接到QProcessreadyReadStandardOutput() 信号并使用QProcessreadStandardOutput() 方法。它的工作就像一个cham。也许这会有所帮助。
    【解决方案2】:

    尽管 Dariusz Scharsig 已经提供了该问题的解决方案,但我想指出我认为可以使用信号槽机制解决的实际问题。

    问题 1. 您的 while 循环中的条件基于 bool QProcess::atEnd () const,这是根据 QProcess Documentation 状态:

    从 QIODevice::atEnd() 重新实现。

    如果进程是返回true 没有运行,并且没有更多数据可供读取;除此以外 返回 false。

    但是,如果您查看 QIODevice::atEnd() 的文档,它会指出:

    如果当前读写位置在末尾,则返回真 设备(即没有更多数据可用于读取 设备);否则返回假。

    对于某些设备,atEnd() 可以返回 即使有更多数据要读取,也是如此。仅此特殊情况 适用于直接响应您生成数据的设备 调用 read()(例如,Unix 和 Mac OS X 上的 /dev 或 /proc 文件,或 所有平台上的控制台输入/标准输入)。

    解决方案 1. 更改 while 循环条件以检查进程状态:while(cmd2.state()!=QProcess::NotRunning){

    问题 2。 您在循环之外使用了 cmd2.waitForReadyRead();。也许有些数据现在可以读取了,当你读完之后,还有一些数据可用:

    • 您阅读了您刚刚编写的命令:ipconfig\n
    • ipconfig 需要一些时间来启动并将文本发送到控制台。但是到那时您已经退出了循环,因为即使您的进程仍在运行,atEnd() 也给出了 true。

    解决方案 2.waitForReadyRead() 放在循环中。

    后果 2.waitForReadyRead() 会告诉您何时有可用数据,这可能不止一行,因此您也应该将cmd2.ReadLine() 更改为cmd2.ReadAll()

    问题 3.QProcess::closeWriteChannel() 中所述

    对于读取输入的程序来说,关闭写入通道是必要的 数据,直到通道关闭。

    解决方案 3. 完成输入输入后,以下选项之一应该可以工作

    • 结束进程:cmd2.write("exit\n");
    • 关闭写入通道:cmd2.closeWriteChannel();

    工作代码:

    #include <QtCore/QCoreApplication>
    #include <QTextStream>
    #include <QByteArray>
    #include <QString>
    #include <QProcess>    
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        QTextStream qout(stdout);
        QByteArray result;
        QProcess cmd2;
    
        cmd2.setReadChannel(QProcess::StandardOutput);
        cmd2.setProcessChannelMode(QProcess::MergedChannels);
        cmd2.start("cmd");
        if (!cmd2.waitForStarted()){
            qout << "Error: Could not start!" << endl;
            return 0;
        }
        cmd2.write("ipconfig\n");
        cmd2.closeWriteChannel();   //done Writing
    
        while(cmd2.state()!=QProcess::NotRunning){
            cmd2.waitForReadyRead();
            result = cmd2.readAll();
            qout << result;
        }
        qout << endl << "---end----" << endl;
        return a.exec();
    }
    

    我写这个答案只是为了解释我理解您的问题并找到解决方案的方式,但想强调首选解决方案是使用Signal/Slot Mechanism as presented by Dariusz

    【讨论】:

    • 嗨,我在 linux 中尝试了相同的代码。仅将“cmd”更改为“xterm”,将“ipconfig”更改为“ifconfig”,但它不起作用。你能帮帮我吗?
    • @ishan3243 嗨,我在 windows 环境中使用 Qt。我现在在 linux 发行版上设置 Qt,尝试了它并得到了相同的结果。所以我也很好奇,所以我调查了一下。根据this Qt bug report xterm 不读取标准输入或向标准输出发送任何内容。因此,监视这些频道,您没有可阅读的内容是正常的。正如this forum thread 建议的那样,使用带有 xterm 的命名管道并在那里进行读写是可行的。
    猜你喜欢
    • 2015-02-04
    • 1970-01-01
    • 2021-12-29
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    相关资源
    最近更新 更多