从执行的应用程序获取标准输出

发布于 2024-12-20 00:58:15 字数 66 浏览 7 评论 0原文

有谁知道如何从 execvp 捕获输出(我认为是标准输出),而不是系统在终端中打印它(在 Linux 上的 c 中)?

Does anyone know how to catch the output (I think its stdout) from execvp instead of the system printing it (in c on linux) in the terminal?

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

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

发布评论

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

评论(5

只是在用心讲痛 2024-12-27 00:58:15

execvp 替换内存中当前正在运行的进程。没有“捕获”输出。

我怀疑您正在尝试从现有进程运行外部进程,并解析其输出。为此,您需要使用 popen() 执行 fork() 然后执行 exec(),返回一个 FILE *< /code> 来读取(这将是您刚刚运行的进程的 stdout)。

execvp replaces the current running process in memory. There's no "catching" the output.

I suspect you're trying to run an external process from an existing process, and parse its output. For that you need to use popen() which does a fork() then an exec(), returning a FILE * to read (which will be the stdout of the process you just ran).

我不信任 popen/pclose,因为我工作过太多系统,其中 SIGCHLD 的处理方式略有不同。而且我不信任 popen 使用的 sh-shell 解析,因为我很少使用它。

O'Reilly 22 年的短书 在 UNIX 上使用 C Dave Curry 的 System 仍然是此类内容的非常很好的参考。

无论如何,这里有一些代码。它有点长,因为它将示例字符串 "/bin/ls /etc" 解析为数组 {"/bin/ls", "/etc", 0}.但我发现使用字符串格式在 98% 的时间里更容易并且更短,尽管这个例子掩盖了这一点。

此代码生成 /etc. 列表。您需要更改一些内容,例如 NUMBER() ,它与 XtNumber() 相同>。您需要确定它是否与您对 SIGCHLD 的处理相匹配。

int main(void) {  // list the files in /etc
   char buf[100];
   FILE *fp;
   int pid = spawnfp("/bin/ls /etc", &fp);
   while (fgets(buf, sizeof buf, fp))
      printf("%s", buf);
   fclose(fp);                    // pclose() replacement
   kill(pid, SIGKILL);            // pclose() replacement
   return 0;
}

这里的子例程是:

static int spawnpipe(const char *argv[], int *fd) // popen() replacement
{
   int pid;
   int pipe_fds[2];

   if (pipe(pipe_fds) < 0)
      FatalError("pipe");

   switch ((pid = fork()))
   {
      case -1:
         FatalError("fork");
      case 0:                     // child
         close(1);
         close(2);
         dup(pipe_fds[0]);
         dup(pipe_fds[1]);
         close(pipe_fds[0]);
         close(pipe_fds[1]);

         execv(argv[0], (char * const *)argv);
         perror("execv");
         _exit(EXIT_FAILURE);    // sic, not exit()
      default:
         *fd = pipe_fds[0];
         close(pipe_fds[1]);
         return pid;
   }
}

这将 ascii 字符串转换为 argv 列表,这对您来说可能没用:

Bool convertStringToArgvList(char *p, const char **argv, int maxNumArgs)
{
   // Break up a string into tokens, on spaces, except that quoted bits, 
   // with single-quotes, are kept together, without the quotes. Such  
   // single-quotes cannot be escaped. A double-quote is just an ordinary char.
   // This is a *very* basic parsing, but ok for pre-programmed strings.
   int cnt = 0;
   while (*p)
   {
      while (*p && *p <= ' ')    // skip spaces
         p++;
      if (*p == '\'')            // single-quote block
      {
         if (cnt < maxNumArgs)
            argv[cnt++] = ++p;   // drop quote
         while (*p && *p != '\'')
            p++;
      }
      else if (*p)               // simple space-delineated token
      {
         if (cnt < maxNumArgs)
            argv[cnt++] = p;
         while (*p > ' ')
            p++;
      }
      if (*p)
         *p++ = 0;               // nul-terminate
   }
   if (cnt < maxNumArgs)
      argv[cnt++] = 0;
   return cnt <= maxNumArgs;     // check for too many tokens (unlikely)
}

这将参数字符串转换为标记,更重要的是,将 fdfp,因为 OP 请求了 stdout

int spawnfp(const char *command, FILE **fpp)
{
   const char *argv[100];
   int fd, pid;
   if (!convertStringToArgvList(strdupa(command), argv, NUMBER(argv)))
      FatalError("spawnfp");
   pid = spawnpipe(argv, &fd);
   *fpp = fdopen(fd, "r");
   return pid;
}

I distrust popen/pclose, as I've worked on too many systems where SIGCHLD was handled slightly differently. And I distrust the sh-shell parsing used by popen, since I rarely use it.

The short 22-year-old O'Reilly book Using C on the UNIX System, by Dave Curry is still a very good reference for this sort of stuff.

Anyway, here is some code. It is a bit lengthy, as it parses the sample string "/bin/ls /etc" into the array {"/bin/ls", "/etc", 0}. But I find using the string format easier and shorter 98% of the time, although this example belies that.

This code generates a listing of /etc. You'll need to change some stuff like e.g. NUMBER() which is the same asXtNumber(). And you'll need to decide whether it matches your handling of SIGCHLD.

int main(void) {  // list the files in /etc
   char buf[100];
   FILE *fp;
   int pid = spawnfp("/bin/ls /etc", &fp);
   while (fgets(buf, sizeof buf, fp))
      printf("%s", buf);
   fclose(fp);                    // pclose() replacement
   kill(pid, SIGKILL);            // pclose() replacement
   return 0;
}

The subroutines here are:

static int spawnpipe(const char *argv[], int *fd) // popen() replacement
{
   int pid;
   int pipe_fds[2];

   if (pipe(pipe_fds) < 0)
      FatalError("pipe");

   switch ((pid = fork()))
   {
      case -1:
         FatalError("fork");
      case 0:                     // child
         close(1);
         close(2);
         dup(pipe_fds[0]);
         dup(pipe_fds[1]);
         close(pipe_fds[0]);
         close(pipe_fds[1]);

         execv(argv[0], (char * const *)argv);
         perror("execv");
         _exit(EXIT_FAILURE);    // sic, not exit()
      default:
         *fd = pipe_fds[0];
         close(pipe_fds[1]);
         return pid;
   }
}

This converts an ascii string to an argv list, which is probably useless to you:

Bool convertStringToArgvList(char *p, const char **argv, int maxNumArgs)
{
   // Break up a string into tokens, on spaces, except that quoted bits, 
   // with single-quotes, are kept together, without the quotes. Such  
   // single-quotes cannot be escaped. A double-quote is just an ordinary char.
   // This is a *very* basic parsing, but ok for pre-programmed strings.
   int cnt = 0;
   while (*p)
   {
      while (*p && *p <= ' ')    // skip spaces
         p++;
      if (*p == '\'')            // single-quote block
      {
         if (cnt < maxNumArgs)
            argv[cnt++] = ++p;   // drop quote
         while (*p && *p != '\'')
            p++;
      }
      else if (*p)               // simple space-delineated token
      {
         if (cnt < maxNumArgs)
            argv[cnt++] = p;
         while (*p > ' ')
            p++;
      }
      if (*p)
         *p++ = 0;               // nul-terminate
   }
   if (cnt < maxNumArgs)
      argv[cnt++] = 0;
   return cnt <= maxNumArgs;     // check for too many tokens (unlikely)
}

This converts the argument string to tokens and, more importantly, the fd to an fp, since the OP requested stdout:

int spawnfp(const char *command, FILE **fpp)
{
   const char *argv[100];
   int fd, pid;
   if (!convertStringToArgvList(strdupa(command), argv, NUMBER(argv)))
      FatalError("spawnfp");
   pid = spawnpipe(argv, &fd);
   *fpp = fdopen(fd, "r");
   return pid;
}
扛起拖把扫天下 2024-12-27 00:58:15

请参阅 popen 的文档,我认为这正是你的需要。

See the documentation of popen, I think it's exactly what you need.

摘星┃星的人 2024-12-27 00:58:15

正如其他人所说,popen 就是您想要使用的。像这样的东西...

#include <iomanip>
#include <iostream>

using namespace std;

const int MAX_BUFFER = 255;

int main()
{
        string cmd;
        cout << "enter cmd: ";
        cin >> cmd;
        cout << endl << "running " << cmd << "…" << endl;


        string stdout;
        char buffer[MAX_BUFFER];
        FILE *stream = popen(cmd.c_str(), "r");
        while ( fgets(buffer, MAX_BUFFER, stream) != NULL )
        stdout.append(buffer);
        pclose(stream);


        cout << endl << "output: " << endl << stdout << endl;
}

As others have said, popen is what you want to use. Something like this...

#include <iomanip>
#include <iostream>

using namespace std;

const int MAX_BUFFER = 255;

int main()
{
        string cmd;
        cout << "enter cmd: ";
        cin >> cmd;
        cout << endl << "running " << cmd << "…" << endl;


        string stdout;
        char buffer[MAX_BUFFER];
        FILE *stream = popen(cmd.c_str(), "r");
        while ( fgets(buffer, MAX_BUFFER, stream) != NULL )
        stdout.append(buffer);
        pclose(stream);


        cout << endl << "output: " << endl << stdout << endl;
}
狠疯拽 2024-12-27 00:58:15

我找到了这个答案,它为 popen 提供了 execvp 风格的界面。

https://codereview.stackexchange.com/questions/31063/popen-with-array- of-参数

I found this answer, which gives popen a execvp style interface.

https://codereview.stackexchange.com/questions/31063/popen-with-array-of-arguments

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文