c++ - 使用 QProcess 将 EndOfText (Ctrl-C) 发送到交互式 shell

标签 c++ bash qt shell qprocess

我使用 QProcess 打开 /bin/sh/usr/bin/bash 并且可以向 shell 写入命令并将输出读入我的程序。

实际问题发生在尝试向 shell 发送文本结束控制信号以中止 shell 正在运行的子进程时。

我尝试过的:

  • shell 以-i交互模式启动
  • 我使用 shell-builtin set -m 命令启用作业控制
  • 出于调试目的,我读出了 $- 变量,它似乎是 himBHs
  • 发送任意命令通常有效(例如 ls)
  • 发送 \x04(传输结束,Ctrl+D)有效并终止 shell。

如何在不再次打开 shell 的情况下适本地终止正在运行的进程?

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

在 shell 中运行第一个命令会在 stdout 上返回输出,但是在发送 ETX 和下一个命令后我不再收到任何东西,尽管 shell 仍在运行 (process.state() == QProcess::运行)

  1. 是否有更好的方法来发送控制信号或与 child 的子进程进行通信?
  2. 在不重新打开 shell 的情况下,我可以做些什么来在 shell 中启动一个新程序? (我问这个的原因是因为程序可能会使用 ssh 作为 shell,我希望它避免为一个小的程序/参数更改重新启动一个全新的连接)

最佳答案

shell 永远看不到 Ctrl-C。它由(伪)终端解释,并转换为 SIGINT ,然后对其进行操作。

在本地,在报告其 pid 的子 shell 中启动程序,然后使用该 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,远程终端将重新解释为正确的信号。

关于c++ - 使用 QProcess 将 EndOfText (Ctrl-C) 发送到交互式 shell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54557411/

相关文章:

c++ - Skia SkCanvas不能用SkBitmap构造

c++ - 将 winrt::com_ptr<ID3D11Device1> 与 D3D11CreateDevice() 一起使用?

c++ - 错误 C2491 : definition of dllimport function not allowed

linux - bash 列出当前文件夹中的子文件夹没有给出预期的结果 [只给出第一个文件夹]

java - 如何用其他语言编写带有绑定(bind)的Qt插件系统?

C++ 模板<基类和派生类>

bash - 在 bash 脚本中从具有相同文件名的不同文件夹加载多个文件

qt - 常见 Qt 问题

c++ - QT无法通过上下文菜单添加新图(QCustomPlot)

linux - 找到一个模式并替换