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

Use QProcess to send EndOfText (Ctrl-C) to interactive shell

本文关键字:交互式 shell Ctrl-C QProcess EndOfText 使用      更新时间:2023-10-16

我使用QProcess打开/bin/sh/usr/bin/bash,可以将命令写入shell并将输出读取到程序中。

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

我尝试过的:

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

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

QProcess process;
process.start("/bin/sh", QStringList() << "-i");
process.write("set -mnecho $-n");                 // returns himBHs
process.waitForBytesWritten();
// start a running program here (E.g. tail -f logfile)
process.write("tail -f logfilen");
process.write("x03");
process.write("newcommandn");
process.waitForBytesWritten();

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

  1. 是否有更好的方式发送控制信号或与孩子的子进程进行通信
  2. 在不重新打开外壳的情况下,我可以做些什么在外壳内启动新程序?(我之所以这么问,是因为程序可能会使用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_ > logn"
"/bin/sh -c 'echo @@@$$@@@>&2; exec tail -f log'n"
"echo donen"
"exitn")
.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=%sn", 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,远程终端将重新解释为正确的信号。