【问题标题】:Grabbing output from exec从 exec 获取输出
【发布时间】:2011-11-09 16:41:22
【问题描述】:

我正在尝试编写一个获取命令输出的 C 程序,然后我会将其传递给另一个程序。

我遇到了一个问题,我不知道如何获取命令输出并存储它。以下是我所拥有的示例

if(fork() == 0){
   execl("/bin/ls", "ls", "-1", (char *)0);
   /* do something with the output here */
}
else{
    //*other stuff goes here*
}

所以基本上我想知道是否有任何方法可以从“execl”获取输出并将其传递给其他东西(例如,通过将其存储在某种缓冲区中)。

建议会很棒。

【问题讨论】:

    标签: c terminal exec fork


    【解决方案1】:

    您必须使用pipe() 创建从父进程到子进程的管道。 然后您必须使用dupdup2standard ouput (STDOUT_FILENO) 和error output (STDERR_FILENO) 重定向到管道,并在父进程中从管道中读取。 它应该可以工作。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define die(e) do { fprintf(stderr, "%s\n", e); exit(EXIT_FAILURE); } while (0);
    
    int main() {
      int link[2];
      pid_t pid;
      char foo[4096];
    
      if (pipe(link)==-1)
        die("pipe");
    
      if ((pid = fork()) == -1)
        die("fork");
    
      if(pid == 0) {
    
        dup2 (link[1], STDOUT_FILENO);
        close(link[0]);
        close(link[1]);
        execl("/bin/ls", "ls", "-1", (char *)0);
        die("execl");
    
      } else {
    
        close(link[1]);
        int nbytes = read(link[0], foo, sizeof(foo));
        printf("Output: (%.*s)\n", nbytes, foo);
        wait(NULL);
    
      }
      return 0;
    }
    

    【讨论】:

    • 非常感谢!展示如何从中读取的出色示例为您赢得了它。
    • 我可以阅读关于 dup2、execl 等的手册页,其中解释了函数调用,但我不了解整体情况。有人可以详细说明为什么我们需要分叉吗?为什么我们在关闭链接的时候关闭了链接,为什么最后会等待(NULL)?
    • 确实可能不需要分叉。但是如果要在智利任务结束时执行操作,则必须 fork 一个新进程。等待确保父进程不会退出bedore child。
    • 我在我的APUE 的副本中找到了这个解释,第 1.6 节:“在子进程中,我们调用 exec 来执行命令 [...]。这将子进程替换为新的程序文件。fork 后跟 exec 的组合称为在某些操作系统上生成新进程。在 UNIX 系统中,这两个部分被分成单独的函数。"
    • 好的,现在一切都开始有意义了。来自手册页:“exec() 系列函数将当前过程映像替换为新的过程映像。”
    【解决方案2】:

    打开一个管道,并更改标准输出以匹配该管道。

     #include <sys/types.h>
     #include <unistd.h>
     #include <stdio.h>
     #include <stdlib.h>
    
     int pipes[2];
    
     pipe(pipes); // Create the pipes
    
     dup2(pipes[1],1); // Set the pipe up to standard output
    

    之后,任何进入标准输出(例如通过 printf)的东西都会从管道 [0] 中出来。

    FILE *input = fdopen(pipes[0],"r");
    

    现在您可以像普通文件描述符一样读取输出。更多详情请看this

    【讨论】:

    • 感谢伟大的 awnser,很抱歉您没有中奖。
    • 在我看来这是一个更清晰的答案,应该是顶级
    【解决方案3】:

    感谢 Jonathan Leffler,我优化了上面的代码,因为它一次无法读取所有响应。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/wait.h>
    
    #define die(e) do { fprintf(stderr, "%s\n", e); exit(EXIT_FAILURE); } while (0);
    
    int main() {
      int link[2];
      pid_t pid;
      char foo[4096 + 1];
      memset(foo, 0, 4096);
    
      if (pipe(link)==-1)
        die("pipe");
    
       if ((pid = fork()) == -1)
        die("fork");
    
      if(pid == 0) {
    
        dup2 (link[1], STDOUT_FILENO);
        close(link[0]);
        close(link[1]);
        execl("/bin/ls", "ls", "-1", (char *)0);
        die("execl");
      } else {
        close(link[1]);
        int nbytes = 0;
        std::string totalStr;
        while(0 != (nbytes = read(link[0], foo, sizeof(foo)))) {
            totalStr = totalStr + foo;
            printf("Output: (%.*s)\n", nbytes, foo);
            memset(foo, 0, 4096);
        }
        wait(NULL);
      }
      return 0;
    }
    

    【讨论】:

      【解决方案4】:

      如果您希望以字符串 (char *) 形式输出,这里有一个选项(至少对于 Linux):

      #include <errno.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/uio.h>
      #include <sys/wait.h>
      #include <unistd.h>
      
      char* qx(char** cmd, int inc_stderr) {
        int stdout_fds[2];
        pipe(stdout_fds);
      
        int stderr_fds[2];
        if (!inc_stderr) {
          pipe(stderr_fds);
        }
      
        const pid_t pid = fork();
        if (!pid) {
          close(stdout_fds[0]);
          dup2(stdout_fds[1], 1);
          if (inc_stderr) {
            dup2(stdout_fds[1], 2);
          }
      
          close(stdout_fds[1]);
      
          if (!inc_stderr) {
            close(stderr_fds[0]);
            dup2(stderr_fds[1], 2);
            close(stderr_fds[1]);
          }
      
          execvp(*cmd, cmd);
          exit(0);
        }
      
        close(stdout_fds[1]);
      
        const int buf_size = 4096;
        char* out = malloc(buf_size);
        int out_size = buf_size;
        int i = 0;
        do {
          const ssize_t r = read(stdout_fds[0], &out[i], buf_size);
          if (r > 0) {
            i += r;
          }
      
          if (out_size - i <= 4096) {
            out_size *= 2;
            out = realloc(out, out_size);
          }
        } while (errno == EAGAIN || errno == EINTR);
      
        close(stdout_fds[0]);
      
        if (!inc_stderr) {
          close(stderr_fds[1]);
          do {
            const ssize_t r = read(stderr_fds[0], &out[i], buf_size);
      
            if (r > 0) {
              i += r;
            }
      
            if (out_size - i <= 4096) {
              out_size *= 2;
              out = realloc(out, out_size);
            }
      
          } while (errno == EAGAIN || errno == EINTR);
      
          close(stderr_fds[0]);
        }
      
        int r, status;
        do {
          r = waitpid(pid, &status, 0);
        } while (r == -1 && errno == EINTR);
      
        out[i] = 0;
      
        return out;
      }
      
      int main() {
        char* argv[3];
        argv[0] = "ls";
        argv[1] = "-la";
        argv[2] = NULL;
        char* out = qx(argv, 0);
        printf("%s", out);
        free(out);
      }
      

      【讨论】:

        猜你喜欢
        • 2015-12-09
        • 1970-01-01
        • 2011-12-30
        • 1970-01-01
        • 2016-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多