使用QProcess中发送文本结束(按Ctrl-C),以交互的shell

问题描述 投票:2回答:2

我用一个QProcess中打开/bin/sh/usr/bin/bash并有可能写命令外壳和读取输出到我的程序。

尝试将结束文本控制信号发送到外壳中止壳的运行子进程时,会出现实际的问题。

我的尝试:

  • 外壳获取-interactive模式启动
  • 我用的是壳内置set -m命令启用作业控制
  • 出于调试目的,我读出了$-变量,它似乎是himBHs
  • 发送任意的命令通常工作(例如ls
  • 发送\x04(传输结束-键,Ctrl + d)的工作原理和杀死壳。

我怎么能杀适当的运行过程中,无需再次打开的壳呢?

QProcess process;
process.start("/bin/sh", QStringList() << "-i");
process.write("set -m\necho $-\n");                 // returns himBHs
process.waitForBytesWritten();

// start a running program here (E.g. tail -f logfile)
process.write("tail -f logfile\n");

process.write("\x03");
process.write("newcommand\n");
process.waitForBytesWritten();

运行在标准输出上输出的外壳内返回的第一个命令,但是我不接受任何发送ETX和下一个命令后了,虽然外壳仍在运行(process.state() == QProcess::Running

  1. 有没有更好的方式来发送控制信号或与孩子的子进程进行沟通?
  2. 我能怎么做可能启动壳内的新程序,而无需再次重启的壳呢? (我问这个的原因是因为该计划很可能将使用ssh作为外壳,我想它,以避免重新启动对未成年人的程序/参数变化的全新的连接)
c++ bash qt shell qprocess
2个回答
1
投票

一个shell不会看到Ctrl-C。它是由(伪)解释末端,并转换为SIGINT,即然后加载。

在当地,在一个子shell,报告其PID启动程序,然后使用该PID直接杀死它。

#include <QtCore>
#include <signal.h>
#include <cstdio>

int getPID(const QByteArray &line) {
   int pid = 0;
   char c1, c2;
   if (sscanf(line.data(), "@@@%d@@%c%c", &pid, &c1, &c2) == 3)
      if (c1 == '@' && (c2 == '\r' || c2 == '\n')) return pid;
   return 0;
}

int main(int argc, char *argv[]) {
   auto input = QByteArray(
                    "echo _kill_me_now_ > log\n"
                    "/bin/sh -c 'echo @@@$$@@@>&2; exec tail -f log'\n"
                    "echo done\n"
                    "exit\n")
                    .split('\n');
   // tail -f will block

   QCoreApplication app(argc, argv);
   QProcess process;
   int pid = 0;

   auto const writeInputLine = [&] {
      if (input.isEmpty()) return;
      auto const line = input.takeFirst();
      puts(line.data());
      fflush(stdout);
      process.write(line);
      process.write("\n");
   };

   process.setProcessChannelMode(QProcess::SeparateChannels);
   QObject::connect(&process, &QProcess::stateChanged, [](auto state) {
      auto static const meta = QMetaEnum::fromType<QProcess::ProcessState>();
      fprintf(stderr, "State=%s\n", meta.key(state));
      fflush(stderr);
      if (state == QProcess::NotRunning) QCoreApplication::quit();
   });
   QObject::connect(&process, &QProcess::readyReadStandardError, [&] {
      auto const data = process.readAllStandardError();
      if (auto p = getPID(data)) pid = p; // we could suppress pid output here
      fputs(data.data(), stdout);
      fflush(stdout);
      if (data.endsWith("$ ")) writeInputLine();
   });
   QObject::connect(&process, &QProcess::readyReadStandardOutput, [&] {
      while (process.canReadLine()) {
         auto const line = process.readLine();
         fputs(line.data(), stdout);
         if (line.startsWith("_kill_me_now_") && pid) {
            kill(pid, SIGTERM);
            pid = 0;
         }
      }
      fflush(stdout);
   });

   process.start("/bin/sh", {"--noediting", "-i"});
   return app.exec();
}

使用ssh,由于需要将信号转发到远程进程,并且因此需要遥控终端(ssh -t)。为此,你将派遣一个Ctrl-C,远程终端将重新解释为一个正确的信号。


-1
投票

你试过用信号和QProcess插槽玩?下面是一个简单的例子:

test::test(QObject* p)
    : QObject (p),
      process{}
{

    connect(&process, SIGNAL(finished(int, QProcess::ExitStatus) ),
                    this, SLOT(hFinish()) );

    process.start("/bin/sh", QStringList() << "-i");
    process.write("set -m\necho $-\n");                 // returns himBHs
    process.waitForBytesWritten();

    // start a running program here (E.g. tail -f logfile)
    process.write("tail -f logfile\n");

    process.write("\x03");
    process.write("newcommand\n");
    process.waitForBytesWritten();

}

void test::hFinish()
{
    this->process.kill();
}
© www.soinside.com 2019 - 2024. All rights reserved.