以最佳方式从 system() 命令捕获标准输出

发布于 2024-07-05 05:15:47 字数 142 浏览 13 评论 0原文

我正在尝试通过 system() 启动外部应用程序 - 例如 system("ls")。 我想在它发生时捕获它的输出,以便我可以将其发送到另一个函数以进行进一步处理。 在 C/C++ 中做到这一点的最佳方法是什么?

I'm trying to start an external application through system() - for example, system("ls"). I would like to capture its output as it happens so I can send it to another function for further processing. What's the best way to do that in C/C++?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

尤怨 2024-07-12 05:15:47

来自 popen 手册:

#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

From the popen manual:

#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);
深空失忆 2024-07-12 05:15:47

尝试 popen() 函数。 它执行一个命令,如 system(),但将输出定向到一个新文件中。 返回指向流的指针。

  FILE *lsofFile_p = popen("lsof", "r");

  if (!lsofFile_p)
  {
    return -1;
  }

  char buffer[1024];
  char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p);
  pclose(lsofFile_p);

Try the popen() function. It executes a command, like system(), but directs the output into a new file. A pointer to the stream is returned.

  FILE *lsofFile_p = popen("lsof", "r");

  if (!lsofFile_p)
  {
    return -1;
  }

  char buffer[1024];
  char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p);
  pclose(lsofFile_p);
↘人皮目录ツ 2024-07-12 05:15:47

编辑:误读问题为想要将输出传递给另一个程序,而不是另一个函数。 popen() 几乎肯定是您想要的。

系统为您提供了对 shell 的完全访问权限。 如果您想继续使用它,您可以
通过 system("ls > tempfile.txt") 将其输出重定向到临时文件,但是选择一个安全的临时文件很痛苦。 或者,您甚至可以通过另一个程序重定向它: system("ls | otherprogram");

有些人可能会推荐 popen() 命令。 如果您可以自己处理输出,这就是您想要的:

FILE *output = popen("ls", "r");

这将为您提供一个 FILE 指针,您可以从中读取命令的输出。

您还可以使用 pipeline() 调用来创建连接,结合 fork() 创建新进程,使用 dup2() 更改它们的标准输入和输出,使用 exec() 运行新程序,使用 wait()在主程序中等待他们。 这只是像 shell 一样设置管道。 有关详细信息和示例,请参阅 pipeline() 手册页。

EDIT: misread question as wanting to pass output to another program, not another function. popen() is almost certainly what you want.

System gives you full access to the shell. If you want to continue using it, you can
redirect it's output to a temporary file, by system("ls > tempfile.txt"), but choosing a secure temporary file is a pain. Or, you can even redirect it through another program: system("ls | otherprogram");

Some may recommend the popen() command. This is what you want if you can process the output yourself:

FILE *output = popen("ls", "r");

which will give you a FILE pointer you can read from with the command's output on it.

You can also use the pipe() call to create a connection in combination with fork() to create new processes, dup2() to change the standard input and output of them, exec() to run the new programs, and wait() in the main program to wait for them. This is just setting up the pipeline much like the shell would. See the pipe() man page for details and an example.

寂寞笑我太脆弱 2024-07-12 05:15:47

函数 popen()pclose() 可能就是您正在寻找的。

查看 glibc 手册< /a> 为例。

The functions popen() and pclose() could be what you're looking for.

Take a look at the glibc manual for an example.

暗地喜欢 2024-07-12 05:15:47

在 Windows 中,不使用 system(),而是使用 CreateProcess,将输出重定向到管道并连接到管道。

我猜这也可以通过某种 POSIX 方式实现?

In Windows, instead of using system(), use CreateProcess, redirect the output to a pipe and connect to the pipe.

I'm guessing this is also possible in some POSIX way?

诺曦 2024-07-12 05:15:47

在此页面中: capture_the_output_of_a_child_process_in_c 描述了使用 popen 与使用 fork/exec 的局限性/dup2/STDOUT_FILENO 方法。

我在 捕获 tshark 输出时遇到问题波彭

我猜这个限制可能是我的问题:

它返回一个 stdio 流而不是一个原始文件描述符,它
不适合异步处理输出。

如果我有其他方法的解决方案,我会回到这个答案。

In this page: capture_the_output_of_a_child_process_in_c describes the limitations of using popen vs. using fork/exec/dup2/STDOUT_FILENO approach.

I'm having problems capturing tshark output with popen.

And I'm guessing that this limitation might be my problem:

It returns a stdio stream as opposed to a raw file descriptor, which
is unsuitable for handling the output asynchronously.

I'll come back to this answer if I have a solution with the other approach.

神爱温柔 2024-07-12 05:15:47

我不完全确定它在标准 C 中是否可能,因为两个不同的进程通常不共享内存空间。 我能想到的最简单的方法是将第二个程序将其输出重定向到文本文件(程序名> textfile.txt),然后读回该文本文件进行处理。 然而,这可能不是最好的方法。

I'm not entirely certain that its possible in standard C, as two different processes don't typically share memory space. The simplest way I can think of to do it would be to have the second program redirect its output to a text file (programname > textfile.txt) and then read that text file back in for processing. However, that may not be the best way.

李不 2024-07-12 05:15:47

其实我刚刚查了一下,:

  1. popen 是有问题的,因为进程是 fork 的。 因此,如果您需要等待 shell 命令执行,那么您就有错过它的危险。 就我而言,我的程序甚至在管道开始工作之前就关闭了。

  2. 我最终在 Linux 上使用了带有 tar 命令的系统调用。 system 的返回值是 tar 的结果。

所以:如果你需要返回值,那么不仅不需要使用popen,它可能不会做你想要的。

Actually, I just checked, and:

  1. popen is problematic, because the process is forked. So if you need to wait for the shell command to execute, then you're in danger of missing it. In my case, my program closed even before the pipe got to do it's work.

  2. I ended up using system call with tar command on linux. The return value from system was the result of tar.

So: if you need the return value, then not no only is there no need to use popen, it probably won't do what you want.

各空 2024-07-12 05:15:47

最有效的方法是直接使用 stdout 文件描述符,绕过 FILE 流:

pid_t popen2(const char *command, int * infp, int * outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if (pipe(p_stdin) == -1)
        return -1;

    if (pipe(p_stdout) == -1) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        return -1;
    }

    pid = fork();

    if (pid < 0) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        close(p_stdout[0]);
        close(p_stdout[1]);
        return pid;
    } else if (pid == 0) {
        close(p_stdin[1]);
        dup2(p_stdin[0], 0);
        close(p_stdout[0]);
        dup2(p_stdout[1], 1);
        dup2(::open("/dev/null", O_WRONLY), 2);

        /// Close all other descriptors for the safety sake.
        for (int i = 3; i < 4096; ++i) {
            ::close(i);
        }

        setsid();
        execl("/bin/sh", "sh", "-c", command, NULL);
        _exit(1);
    }

    close(p_stdin[0]);
    close(p_stdout[1]);

    if (infp == NULL) {
        close(p_stdin[1]);
    } else {
        *infp = p_stdin[1];
    }

    if (outfp == NULL) {
        close(p_stdout[0]);
    } else {
        *outfp = p_stdout[0];
    }

    return pid;
}

要从 child 读取输出,请使用 popen2()< /code> 像这样:

int child_stdout = -1;
pid_t child_pid = popen2("ls", 0, &child_stdout);

if (!child_pid) {
    handle_error();
}

char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));

写入和读取:

int child_stdin = -1;
int child_stdout = -1;
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout);

if (!child_pid) {
    handle_error();
}

const char text = "1\n2\n123\n3";
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1);

char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));

The most efficient way is to use stdout file descriptor directly, bypassing FILE stream:

pid_t popen2(const char *command, int * infp, int * outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if (pipe(p_stdin) == -1)
        return -1;

    if (pipe(p_stdout) == -1) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        return -1;
    }

    pid = fork();

    if (pid < 0) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        close(p_stdout[0]);
        close(p_stdout[1]);
        return pid;
    } else if (pid == 0) {
        close(p_stdin[1]);
        dup2(p_stdin[0], 0);
        close(p_stdout[0]);
        dup2(p_stdout[1], 1);
        dup2(::open("/dev/null", O_WRONLY), 2);

        /// Close all other descriptors for the safety sake.
        for (int i = 3; i < 4096; ++i) {
            ::close(i);
        }

        setsid();
        execl("/bin/sh", "sh", "-c", command, NULL);
        _exit(1);
    }

    close(p_stdin[0]);
    close(p_stdout[1]);

    if (infp == NULL) {
        close(p_stdin[1]);
    } else {
        *infp = p_stdin[1];
    }

    if (outfp == NULL) {
        close(p_stdout[0]);
    } else {
        *outfp = p_stdout[0];
    }

    return pid;
}

To read output from child use popen2() like this:

int child_stdout = -1;
pid_t child_pid = popen2("ls", 0, &child_stdout);

if (!child_pid) {
    handle_error();
}

char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));

To both write and read:

int child_stdin = -1;
int child_stdout = -1;
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout);

if (!child_pid) {
    handle_error();
}

const char text = "1\n2\n123\n3";
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1);

char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));
和影子一齐双人舞 2024-07-12 05:15:47

函数 popen() 等不会重定向 stderr 等; 为此,我编写了 popen3()

这是我的 popen3() 的鲍德勒版本:

int popen3(int fd[3],const char **const cmd) {
        int i, e;
        int p[3][2];
        pid_t pid;
        // set all the FDs to invalid
        for(i=0; i<3; i++)
                p[i][0] = p[i][1] = -1;
        // create the pipes
        for(int i=0; i<3; i++)
                if(pipe(p[i]))
                        goto error;
        // and fork
        pid = fork();
        if(-1 == pid)
                goto error;
        // in the parent?
        if(pid) {
                // parent
                fd[STDIN_FILENO] = p[STDIN_FILENO][1];
                close(p[STDIN_FILENO][0]);
                fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
                close(p[STDOUT_FILENO][1]);
                fd[STDERR_FILENO] = p[STDERR_FILENO][0];
                close(p[STDERR_FILENO][1]);
                // success
                return 0;
        } else {
                // child
                dup2(p[STDIN_FILENO][0],STDIN_FILENO);
                close(p[STDIN_FILENO][1]);
                dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
                close(p[STDOUT_FILENO][0]);
                dup2(p[STDERR_FILENO][1],STDERR_FILENO);
                close(p[STDERR_FILENO][0]);
                // here we try and run it
                execv(*cmd,const_cast<char*const*>(cmd));
                // if we are there, then we failed to launch our program
                perror("Could not launch");
                fprintf(stderr," \"%s\"\n",*cmd);
                _exit(EXIT_FAILURE);
        }

        // preserve original error
        e = errno;
        for(i=0; i<3; i++) {
                close(p[i][0]);
                close(p[i][1]);
        }
        errno = e;
        return -1;
}

The functions popen() and such don't redirect stderr and such; I wrote popen3() for that purpose.

Here's a bowdlerised version of my popen3():

int popen3(int fd[3],const char **const cmd) {
        int i, e;
        int p[3][2];
        pid_t pid;
        // set all the FDs to invalid
        for(i=0; i<3; i++)
                p[i][0] = p[i][1] = -1;
        // create the pipes
        for(int i=0; i<3; i++)
                if(pipe(p[i]))
                        goto error;
        // and fork
        pid = fork();
        if(-1 == pid)
                goto error;
        // in the parent?
        if(pid) {
                // parent
                fd[STDIN_FILENO] = p[STDIN_FILENO][1];
                close(p[STDIN_FILENO][0]);
                fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
                close(p[STDOUT_FILENO][1]);
                fd[STDERR_FILENO] = p[STDERR_FILENO][0];
                close(p[STDERR_FILENO][1]);
                // success
                return 0;
        } else {
                // child
                dup2(p[STDIN_FILENO][0],STDIN_FILENO);
                close(p[STDIN_FILENO][1]);
                dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
                close(p[STDOUT_FILENO][0]);
                dup2(p[STDERR_FILENO][1],STDERR_FILENO);
                close(p[STDERR_FILENO][0]);
                // here we try and run it
                execv(*cmd,const_cast<char*const*>(cmd));
                // if we are there, then we failed to launch our program
                perror("Could not launch");
                fprintf(stderr," \"%s\"\n",*cmd);
                _exit(EXIT_FAILURE);
        }

        // preserve original error
        e = errno;
        for(i=0; i<3; i++) {
                close(p[i][0]);
                close(p[i][1]);
        }
        errno = e;
        return -1;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文