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

时间:2019-02-06 15:44:32

标签: c++ bash qt shell qprocess

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

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

我尝试过的事情:

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

在外壳程序中运行第一个命令会在stdout上返回输出,但是尽管外壳程序仍在运行(process.state() == QProcess::Running),但在发送ETX和下一个命令后我什么也没收到

  1. 是否有更好的方法发送控制信号或与孩子的孩子的孩子进行交流?
  2. 如何在不重新打开外壳的情况下在外壳内启动新程序? (我之所以这样问,是因为该程序可能将使用ssh作为外壳,并且我希望它避免为较小的程序/参数更改而重新启动全新的连接)

2 个答案:

答案 0 :(得分:1)

shell从没见过#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(); } 。它由(伪)终端解释,并转换为SIGINT,然后对其起作用。

在本地,在报告其pid的子外壳中启动程序,然后使用该PID直接将其杀死。

ssh -t

使用ssh,由于您需要将信号转发到远程进程,因此需要一个 remote 控制终端(Ctrl-C)。而对于 发送volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro ,远程终端将重新解释为适当的信号。

答案 1 :(得分:-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();
}