调用外部程序并重定向输入输出(Linux C++)

在 Linux 中调用外部程序并重定向输入输出。

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

using namespace std;

// 该函数调用 cmd 程序,并传入 argv 参数列表,使用 envp 环境变量
// 如果 fin  >= 0,则会通过 fin  返回一个文件描述符,是 cmd 重定向后的标准输入
// 如果 fout >= 0,则会通过 fout 返回一个文件描述符,是 cmd 重定向后的标准输出
// 如果 ferr >= 0,则会通过 ferr 返回一个文件描述符,是 cmd 重定向后的标准错误
// background 表示 cmd 是否在后台运行,不等待其运行结束
// argv 和 envp 是字符串数组,字符串以 \0 结尾,数组以空字符串结尾
// argv 第一个元素必须是要执行的程序本身,第二个元素开始才是要传入的参数
// environ 是当前进程的环境变量,可以通过 getenv、putenv、setenv、unsetenv 读写
int run_ex(const char *cmd, char *const *argv, int &fin, int &fout, int &ferr,
           bool background = false, char **envp = environ)
{
    // 初始化为 -1,因为要用其值判断管道是否创建成功
    int fdin [2] = {-1, -1};
    int fdout[2] = {-1, -1};
    int fderr[2] = {-1, -1};

    try
    {
        // 如果 fin >= 0,则创建 fdin 管道,并将管道的一端通过 fin 返回
        if (fin >= 0) {
            if (pipe(fdin) == 0)
                fin = fdin[1];
            else {
                perror("pipe(fdin)");
                throw(-1);
            }
        }

        // 如果 fout >= 0,则创建 fdout 管道,并将管道的一端通过 fout 返回
        if (fout >= 0) {
            if (pipe(fdout) == 0)
                fout = fdout[0];
            else {
                perror("pipe(fdout)");
                throw(-1);
            }
        }

        // 如果 ferr >= 0,则创建 fderr 管道,并将管道的一端通过 ferr 返回
        if (ferr >= 0) {
            if (pipe(fderr) == 0)
                ferr = fderr[0];
            else {
                perror("pipe(fderr)");
                throw(-1);
            }
        }

        // 创建子进程
        pid_t pid = fork();

        // 子进程创建失败
        if (pid < 0)
            throw(-1);

        if (pid == 0) {
            // 子进程要执行的代码
            if (fin  >= 0) close(fin);
            if (fout >= 0) close(fout);
            if (ferr >= 0) close(ferr);

            // 将 fdin[0] 文件描述符复制给 stdin,然后关闭 fdin[0]
            if (fin >= 0 && fdin[0] != STDIN_FILENO) {
                if (dup2(fdin[0], STDIN_FILENO) != STDIN_FILENO) {
                    perror("dup2(fdin[0], stdin)");
                    _Exit(-1);
                } else
                    close(fdin[0]);
            }

            // 将 fdout[1] 文件描述符复制给 stdout,然后关闭 fdout[1]
            if (fout >= 0 && fdout[1] != STDOUT_FILENO) {
                if (dup2(fdout[1], STDOUT_FILENO) != STDOUT_FILENO) {
                    perror("dup2(fdout[1], stdout)");
                    _Exit(-1);
                } else
                    close(fdout[1]);
            }

            // 将 fderr[1] 文件描述符复制给 stderr,然后关闭 fderr[1]
            if (ferr >= 0 && fderr[1] != STDERR_FILENO) {
                if (dup2(fderr[1], STDERR_FILENO) != STDERR_FILENO) {
                    perror("dup2(fderr[1], stderr)");
                    _Exit(-1);
                } else
                    close(fderr[1]);
            }

            // 子进程要执行的代码
            // Exec 函数族命名规律:
            // l (list)        使用命令行参数列表
            // v (vector)      使用命令行参数数组
            // p (path)        从 PATH 中搜索程序
            // e (environment) 传入自定义环境变量
            execvpe(cmd, argv, envp);

            // 如果命令启动成功,则不会到达这里,到达这里表示命令启动失败,
            // 比如命令不存在,参数格式错误等
            // 此时不能用 exit 返回,要直接调用 _Exit 中止程序,否则子进程
            // 会退回到 main 函数继续执行
            _Exit(-1);
        }
        else if (pid > 0) {
            // 父进程要执行的代码
            if (fin  >= 0) { close(fdin [0]); fdin [1] = -1; }
            if (fout >= 0) { close(fdout[1]); fdout[0] = -1; }
            if (ferr >= 0) { close(fderr[1]); fderr[0] = -1; }

            if (background)
                // 如果不等待,则直接返回子进程的 pid
                return pid;
            else {
                // 等待任意一个子进程结束
                // 正常情况下,如果不等待子进程结束,则当父进程结束后,子进程会继续运行。
                // 调试模式下父进程执行完毕,可能会杀死子进程。
                wait(&pid);

                // 如果子进程成功退出
                if (WIFEXITED(pid))
                    // 返回退出码
                    return WEXITSTATUS(pid);
                else
                    // 否则返回 -1
                    return -1;
            }
        }
    }
    catch (int n)
    {
		// 这些异常都是在创建子进程之前出现的
        if (fdin [0] >= 0) { close(fdin [0]); }
        if (fdout[1] >= 0) { close(fdout[1]); }
        if (fderr[1] >= 0) { close(fderr[1]); }

        if (fin  >= 0) { close(fin ); fin  = -1; }
        if (fout >= 0) { close(fout); fout = -1; }
        if (ferr >= 0) { close(ferr); ferr = -1; }

        return n;
    }
    return -1;
}

int run(string cmd, char *const *argv)
{
    int fin = -1, fout = -1, ferr = -1;
    return run_ex(cmd.c_str(), argv, fin, fout, ferr);
}

int run_in(string cmd, char *const *argv, string sin, char **envp = environ)
{
    int fin = 0, fout = -1, ferr = -1;
    int ret = run_ex(cmd.c_str(), argv, fin, fout, ferr, true, envp);

    if (ret >= 0)
        write(fin, sin.c_str(), sin.length());

    close(fin);

    return ret;
}

char *run_out(string cmd, char *const *argv, char **envp = environ)
{
    int fin = -1, fout = 0, ferr = -1;
    int ret = run_ex(cmd.c_str(), argv, fin, fout, ferr, false, envp);

    char *s;
    if (ret >= 0) {
        const int BUFSIZE = 1024;

        int len = BUFSIZE;
        s = (char *)malloc(len);

        int n = read(fout, s, BUFSIZE);
        while (n == BUFSIZE) {
            s = (char *)realloc(s, len + BUFSIZE);
            n = read(fout, s + len, BUFSIZE);
            len += n;
        };
        s = (char *)realloc(s, len + 1);
        s[len] = 0;
    }
    close(fout);

    return s;
}

int main(void)
{
    char *const args1[4] = {(char *)"echo", (char *)"Hello   world", (char *)"!", NULL};
    run("echo", args1);

    char *const args2[2] = {(char *)"cat", NULL};
    run_in("cat", args2, "Hello world!\n", NULL);

    char *const args3[2] = {(char *)"ls", NULL};
    char *s = run_out("ls", args3, NULL);
    printf("%s", s);
    free(s);

    return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC(Microsoft Foundation Class)是一种基于Microsoft Windows操作系统的C++应用程序开发框架。MFC可以用于开发Windows图形用户界面(GUI)应用程序。 要通过MFC调用一个外部的可执行文件(exe),可以使用MFC中的一些函数来实现。其中一个常用的函数是CreateProcess,它可以启动一个外部进程。 要重定向标准输出,可以使用CreatePipe函数来创建一个匿名管道,然后将管道的写入端作为子进程的标准输出。这样子进程的输出就会被发送到管道中。 首先,在MFC应用程序中,我们需要包含windows.h头文件,以便使用CreateProcess函数。然后,我们可以通过以下代码来调用exe并重定向标准输出: ```cpp #include <windows.h> #include <iostream> void RedirectOutput() { SECURITY_ATTRIBUTES saAttr; HANDLE hChildStdoutRd, hChildStdoutWr; HANDLE hChildStdoutWrDup = NULL; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) { std::cout << "Error creating pipe" << std::endl; return; } if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutWr, GetCurrentProcess(),&hChildStdoutWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) { std::cout << "Error duplicating handle" << std::endl; return; } STARTUPINFOA si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.hStdError = hChildStdoutWrDup; si.hStdOutput = hChildStdoutWrDup; si.dwFlags |= STARTF_USESTDHANDLES; if (!CreateProcessA(NULL, "your_exe.exe", NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { std::cout << "Error creating process" << std::endl; return; } CloseHandle(hChildStdoutWrDup); CloseHandle(hChildStdoutWr); char buffer[4096]; DWORD bytesRead; while (ReadFile(hChildStdoutRd, buffer, sizeof(buffer), &bytesRead, NULL)) { if (bytesRead == 0) { break; } std::cout.write(buffer, bytesRead); } CloseHandle(hChildStdoutRd); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } int main() { RedirectOutput(); return 0; } ``` 在这个例子中,我们首先创建了一个匿名管道,并将管道的写入端重定向到子进程的标准输出和标准错误。然后使用CreateProcess函数启动了一个名为"your_exe.exe"的外部可执行文件。最后,我们通过循环读取管道中的数据,并将其输出到控制台。 这样,我们就可以通过MFC调用一个外部的可执行文件,并将其输出重定向到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值