【问题标题】:Cannot execute echo command in QProcess无法在 QProcess 中执行 echo 命令
【发布时间】:2013-11-08 06:22:55
【问题描述】:

我想在我的设备中使用带有 Qt 的 Ubuntu 10.04 LTS 下的 netcat 实用程序启动一个 SCPI 命令。我的代码如下:

env = "echo TRIG | nc 192.168.1.100 23 -q1";
process1.execute(env);
process1.waitForFinished(1000);

此命令不返回任何数据,只是触发数据采集。 如果使用具有相同“echo TRIG | nc 192.168.1.100 23 -q1”命令的终端,一切正常。 从 Qt 开始,它不起作用。调试输出是“TRIG | nc 10.0.3.250 23 -q1”......所以没有“回声”。我的设备没有收到 TRIG 命令。

你能告诉我我做错了什么吗? 非常感谢。

【问题讨论】:

  • 当 Qt 原生支持通过 TCP/IP 套接字发送数据时,您到底为什么要这样做?打开连接并发送简单的消息很容易。

标签: qt echo qprocess


【解决方案1】:

下面的代码显示了此功能的相当完整的异步实现。它演示了如何在不启动外部进程的情况下完成它,以及如何在 Qt 5 中利用 C++11 lambda。对于 Qt 4,来自 main() 的插槽需要存在于它们自己的 QObject 派生类中。

#include <QtWidgets>
#include <QtNetwork>

class SocketSignaler : public QObject
{
   Q_OBJECT
   Q_SLOT void stateChanged(QAbstractSocket::SocketState state) {
      if (state == QAbstractSocket::UnconnectedState) emit unconnected();
      else emit busy();
      emit hasState(this->state());
   }
public:
   explicit SocketSignaler(QAbstractSocket * socket) : QObject(socket) {
      connect(socket, &QAbstractSocket::stateChanged, this, &SocketSignaler::stateChanged);
      connect(&(const QObject&)QObject(), &QObject::destroyed, this, // defer signal emission
              [=]{ emit stateChanged(socket->state()); }, Qt::QueuedConnection);
   }
   Q_SIGNAL void busy();
   Q_SIGNAL void unconnected();
   Q_SIGNAL void hasState(const QString &);
   QString state() const {
      switch (static_cast<QAbstractSocket*>(parent())->state()) {
      case QAbstractSocket::UnconnectedState: return "Disconnected";
      case QAbstractSocket::HostLookupState: return "Looking up host";
      case QAbstractSocket::ConnectingState: return "Connecting";
      case QAbstractSocket::ConnectedState: return "Connected";
      case QAbstractSocket::ClosingState: return "Closing";
      default: return {};
      }
   }
};

class Ui : public QWidget {
   Q_OBJECT
   Q_PROPERTY(bool busy WRITE setBusy)
   QVBoxLayout m_layout{this};
   QFormLayout m_form;
   QLineEdit m_target{"192.168.1.100"};
   QLineEdit m_message{"TRIG"};
   QLabel m_state;
   QDialogButtonBox m_box;
   QPushButton * const m_send = m_box.addButton("Send", QDialogButtonBox::AcceptRole);
   QPushButton * const m_cancel = m_box.addButton(QDialogButtonBox::Cancel);
   QMessageBox m_msgBox{this};
public:
   Ui() {
      m_form.addRow("Target Host", &m_target);
      m_form.addRow("Command", &m_message);
      m_layout.addLayout(&m_form);
      m_layout.addWidget(&m_state);
      m_layout.addWidget(&m_box);
      m_msgBox.setIcon(QMessageBox::Critical);
      connect(m_send, &QPushButton::clicked, this, &Ui::send);
      connect(m_cancel, &QPushButton::clicked, this, &Ui::cancel);
   }
   void setState(const QString & text) { m_state.setText(text); }
   QString target() const { return m_target.text(); }
   QString message() const { return m_message.text(); }
   void showError(const QString & text) {
      m_msgBox.setText(text);
      m_msgBox.show();
   }
   void setBusy(bool busy) {
      m_send->setEnabled(!busy);
      m_cancel->setEnabled(busy);
   }
   Q_SIGNAL void send();
   Q_SIGNAL void cancel();
};

int main(int argc, char *argv[])
{
   const int targetPort = 23;
   QApplication app{argc, argv};
   Ui ui;
   ui.show();

   QTcpSocket socket;
   SocketSignaler socketSig{&socket};
   QObject::connect(&socketSig, &SocketSignaler::hasState, &ui, &Ui::setState);

   QStateMachine machine;
   QState sReady{&machine};
   QState sBusy{&machine};
   sReady.assignProperty(&ui, "busy", false);
   sBusy.assignProperty(&ui, "busy", true);
   sReady.addTransition(&socketSig, &SocketSignaler::busy, &sBusy);
   sBusy.addTransition(&socketSig, &SocketSignaler::unconnected, &sReady);

   QObject::connect(&ui, &Ui::send, [&](){
      socket.connectToHost(ui.target(), targetPort);
   });
   QObject::connect(&ui, &Ui::cancel, [&](){ socket.abort(); });
   QObject::connect(&socket,
                    static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>
                    (&QAbstractSocket::error), [&]()
   {
      ui.showError(socket.errorString());
   });
   QObject::connect(&socket, &QAbstractSocket::connected, [&](){
      auto msg = ui.message().toLatin1();
      msg.append('\n');
      if (socket.write(msg) >= msg.size()) socket.close();
   });
   QObject::connect(&socket, &QAbstractSocket::bytesWritten, [&](){
      if (!socket.bytesToWrite()) socket.close();
   });

   machine.setInitialState(&sReady);
   machine.start();
   return app.exec();
}
#include "main.moc"

【讨论】:

    【解决方案2】:

    您不能以这种方式将管道命令 (|) 与 QProcess 一起使用。

    有几种方法可以解决这个问题:-

    您可以调用第一个命令并检索其输出,然后在 Qt 中或通过另一个调用 QProcess 进行处理。

    或者,创建一个从 QProcess 调用的脚本并检索输出。

    最后,假设您使用的是 linux / OSX,您可以使用 /bin/bash 调用 QProcess 并将命令传递给它。例如:-

    env = "/bin/bash \"echo TRIG | nc 192.168.1.100 23 -q1\"";
    process1.execute(env);
    

    你可能会找到一个相当于 /bin/bash for windows,也许是 cmd.exe

    【讨论】:

    • 感谢您的提示。但是,它返回以下内容: /bin/bash: echo TRIG | nc 192.168.1.100 23 -q1:没有这样的文件或目录这可能是不同的东西(路径?)
    • 什么是'TRIG',是脚本还是程序?我不认为这是一个标准的 bash 调用。
    • TRIG 只是一个 SCPI 命令,用于触发我的远程设备上的数据采集。
    • 你可以使用命令的完整路径吗?估计是没有设置环境路径,这种情况下,也可以在调用exec之前设置进程环境。
    • 在我看来“回声”是个问题。如果我尝试仅使用 env = "/bin/bash \"echo abc\"" 它会返回相同的错误。也许我不明白 /bin/bash 的语法。 “TRIG”命令是 SCPI 命令,没有完整路径。我想我正确地使用了它。如果这个不起作用,我会试试你的另外两个建议(但这是最方便的一个)。
    最近更新 更多