提问者:小点点

在控制台应用程序中运行外部文本编辑器-C++


我正在用C++构建一个控制台应用程序,需要一种方法来调用终端文本编辑器,以满足用户的编辑乐趣,并知道何时完成。

例如,在git中,当您运行git rebase-interactive时,它会在终端中启动一个文本编辑器(默认情况下是Nano),您可以轻松地在其中编辑提交。当您关闭编辑器时,git将继续在控制台中进行操作。

我相信我需要做的是将一个编辑器作为子进程启动,不断地通过cincout传递给它,最后在编辑器退出时恢复程序。

我研究了popen,但它只发送一个流(stdout)。我甚至通读了Git的重基实现,但搞不清楚他们是怎么做到的。

有什么建议吗?


共1个答案

匿名用户

如何调用编辑器取决于系统。在Unix系统上,您将生成一个进程,并将标准输入、标准输出和标准错误传递给子进程,因为可能所有这三个进程都连接到终端。如果不重定向这些流,这通常是默认行为。

这通常是通过使用fork和Unix上的exec*系列函数之一来完成的,但也可以使用posix_spawn系列函数。在Windows上有不同的方法。

如果已设置visual环境变量,并且term设置为dumb以外的其他内容,则应使用该环境变量,否则应使用editor;如果两者都未设置,则返回到系统默认值(通常为vi)。这些环境变量的值必须始终传递给/bin/sh(如果不是POSIX sh,则传递给POSIX sh)以求值;例如,将visualeditor设置为f(){vim“$0”“$@”;};f总是可以接受的,并且使用这些变量的所有程序都必须支持这一点。Git也会做同样的事情,还会在其他位置搜索编辑器。

下面是一个粗略的C(和有效的C++)程序,它什么也不做,只是通过命令行中的参数生成一个编辑器。它应该大致演示如何正确生成编辑器:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

bool is_dumb(void)
{
    const char *term = getenv("TERM");
    return !term || !strcmp(term, "dumb");
}

const char *editor(void)
{
    const char *ed = NULL;
    if (!is_dumb())
        ed = getenv("VISUAL");
    if (!ed)
        ed = getenv("EDITOR");
    if (!ed)
        ed = "vi";
    return ed;
}

int main(int argc, char **argv)
{
    const char *ed = editor();
    pid_t pid = fork();
    if (pid < 0) {
        perror("Failed to spawn editor");
        exit(127);
    }
    if (!pid) {
        const char *append = " \"$@\"";
        size_t len = strlen(ed) + strlen(append) + 1;
        char *final_editor = (char *)malloc(len);
        snprintf(final_editor, len, "%s%s", ed, append);
        const char **args = (const char **)malloc(sizeof(const char *) * (argc + 3));
        if (!args)
            exit(255);
        args[0] = "sh";
        args[1] = "-c";
        args[2] = final_editor;
        for (int i = 1; i < argc; i++)
            args[i + 2] = argv[i];
        args[argc + 2] = NULL;
        execvp("sh", (char *const *)args);
        exit(127);
    } else {
        int wstatus;
        waitpid(pid, &wstatus, 0);
        if (WIFEXITED(wstatus)) {
            exit(wstatus);
        } else {
            exit(255);
        }
    }
}

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(控制台|应用程序|中|运行|外部|文本|编辑器|c++)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?