QProcess问题,进程输出

6
我正在尝试弄清楚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();
}

上述代码的输出是:

hello

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
---end----
应该更像这样(带有ipconfig结果)。

Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\noname>ipconfig

Windows IP Configuration

Ethernet adapter Local Area Connection:

    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 Igor Pavlov 2010-06-20
用法: 7z [...]
<archive_name> [<file_names>...] [<@listfiles...>]
a: 将文件添加到压缩包中
b: 压力测试 d: 从压缩包中删除文件 e: 将文件从压缩包中解压出来(不使用目录名称) l: 显示压缩包的内容 t: 测试压缩包的完整性 u: 更新压缩包中的文件 x: 解压带完整路径的文件
-ai[r[-|0]]{@listfile|!wildcard}: 包含压缩包 -ax[r[-|0]]{@listfile|!wildcard}: 排除压缩包 -bd: 禁用百分比显示
-i[r[-|0]]{@listfile|!wildcard}: 包含文件名 -m{参数}: 设置压缩方式 -o{目录}: 设置输出目录 -p{密码}: 设置密码 -r[-|0]: 递归子目录 -scs{UTF-8 | WIN | DOS}: 设置列表文件的字符集 -sfx[{名称}]: 创建自解压缩包 -si[{名称}]: 从 stdin 中读取数据 -slt: 显示 l 命令的技术信息 -so: 将数据写入 stdout -ssc[-]: 设置大小写敏感模式 -ssw: 压缩共享文件
-t{类型}: 设置压缩包类型 -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]: 更新选项 -v{大小}[b|k|m|g]: 创建分卷 -w[{路径}]: 指定工作目录,空路径表示临时目录
-x[r[-|0]]]{@listfile|!wildcard}: 排除文件名 -y: 在所有提示中都选择“是”
2个回答

4
尽管Dariusz Scharsig已经提供了解决方案,但我想指出我认为实际问题的根源,可以使用信号槽机制来解决。 问题1。 while循环中的条件基于bool QProcess::atEnd() const,根据QProcess文档,它表示:

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

如果进程未运行并且没有更多可读取的数据,则返回true;否则返回false。

但是,如果您查看QIODevice :: atEnd()的文档,则会说明:
如果当前读写位置在设备末尾(即没有更多可用数据供设备读取),则返回true;否则返回false。
对于一些设备,即使还有更多数据可以读取,atEnd()也可能返回true。这种特殊情况仅适用于在您调用read()时直接生成数据的设备(例如Unix和Mac OS X上的/dev或/proc文件,以及所有平台上的控制台输入/stdin)。
解决方案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();
}

我写这篇回答只是为了解释我理解你的问题的方式并找到一种解决方案,但我想强调首选解决方案是使用Dariusz提出的Signal/Slot机制

嗨,我在Linux中尝试了相同的代码。只是将“cmd”更改为“xterm”,并且将“ipconfig”更改为“ifconfig”,但它不起作用。请问你能帮我吗? - ishan3243
@ishan3243 你好,我之前在Windows环境下使用Qt。现在我在Linux发行版上安装了Qt,并尝试了一下,但是得到了相同的结果。所以我进行了一些调查,因为我也很好奇。根据这个Qt错误报告,xterm不会读取stdin或向stdout发送任何东西。因此,监视这些信道,你将无法读取任何信息。正如这个论坛帖子所建议的那样,在xterm中使用命名管道并在那里读写可能会有用。 - Pau Coma Ramirez

4
我看到一个大问题。 在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"

希望这能帮到你。在我的机器上,每次都起作用。


谢谢,但似乎没有什么区别。我已经意识到一件事——有时候带有\r和不带\r都可以工作。如果我运行应用程序10次,也许50%的时间它可以工作,50%的时间它不能工作。复制提示:在重新运行应用程序之间等待不同的时间。 - user440297
1
我无法告诉你确切的原因,但我尝试了不同的方法。没有使用waitForReadyRead(),而是连接到QProcessreadyReadStandardOutput()信号,并使用QProcessreadStandardOutput()方法。它像魔法一样奏效了。也许这会有所帮助。 - Dariusz Scharsig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接