【问题标题】:How to redirect the output of a system call to inside the program in C/C++?如何将系统调用的输出重定向到 C/C++ 中的程序内部?
【发布时间】:2011-02-08 23:13:35
【问题描述】:

我正在用 C++ 编写一个程序,它对 Linux 操作系统上当前目录中的所有文件进行一些特殊处理。

所以我正在考虑使用诸如system("ls") 之类的系统调用来获取所有文件的列表。

但是如何将它存储在我的程序中? (如何将 ls 的输出重定向到我在程序中声明的字符串)

谢谢

【问题讨论】:

    标签: c++ string redirect system call


    【解决方案1】:

    我建议你不要调用ls - 正确地完成工作,使用opendir/readdir/closedir 直接读取目录。

    代码示例,打印目录条目:

    // $ gcc *.c && ./a.out 
    #include <stdlib.h> // NULL
    #include <stdio.h>  
    #include <dirent.h>
    
    int main(int argc, char* argv[]) {
      const char* path = argc <= 1 ? "." : argv[1];
    
      DIR* d = opendir(path);
      if (d == NULL) return EXIT_FAILURE;
    
      for(struct dirent *de = NULL; (de = readdir(d)) != NULL; )
        printf("%s/%s\n", path, de->d_name);
    
      closedir(d);
      return 0;
    }
    

    【讨论】:

    • 请注意,您不应该在多线程程序中使用readdir。相反,您应该使用readdir_r
    【解决方案2】:

    我认为您不能使用系统来读取输出。

    尝试使用popen

       #include <stdio.h>
    
       FILE *popen(const char *command, const char *type);
    
       int pclose(FILE *stream);
    

    但是,您可能不想为ls 执行此操作。两个原因:

    • 如果您想获取目录列表,请直接使用文件系统 API (readdir),而不是使用 shell 命令并对其进行解析。
    • 最近有人向我指出了 blog post,它解释了为什么解析 ls 输出是一个坏主意。

    【讨论】:

    • 我认为这是一个很好的补充,可以解释为什么 ls 不起作用。
    • @RaphaelSP:我看到另一个描述不使用 ls 的答案,但想回答系统与流行问题。不过,我已经提出了一个更好的理由。
    【解决方案3】:

    您使用的是 UNIX 还是 Windows?在 UNIX 上,您将使用 fork() 调用创建一个子进程,然后创建一些管道并将子进程的 STDOUT 分配给父进程的 STDIN。在 Windows 上,Win32 API 中肯定会有类似的东西。

    【讨论】:

    【解决方案4】:

    我看到了 3 个选项:

    1. 执行system("ls &gt; tempfile"),然后从临时文件中读取ls 的输出
    2. 使用pipe() 打开一个管道,然后使用fork() 生成一个新进程。在子进程中关闭文件描述符 2 并通过调用dup() 替换它的写入管道结束。接下来,在子进程中调用exec("ls",...)。最后从父进程的管道中读取ls的输出
    3. 不要使用ls 枚举当前目录中的文件。您的程序中有一些功能可以做到这一点,即opendir(),readdir(),closedir

    我强烈推荐选项 3。

    【讨论】:

      【解决方案5】:

      要么使用适当的 c 调用来读取目录中的文件,要么概括您的程序,以便它将文件列表作为输入并将该列表从 ls 管道传输到您的程序

      > ls | yoursoftware
      

      【讨论】:

        【解决方案6】:

        一个简单的方法是使用boost filesystem 目录迭代器。或者像其他人建议的那样使用 opendir/readdir/closedir 。你前进的方向没有真正的好处。

        代码示例,指定目录中常规文件的打印大小:

        // $ g++ listdir.cc -o listdir -lboost_filesystem && ./listdir
        #include <iostream>
        #include <boost/filesystem.hpp>
        
        int main(int argc, char* argv[]) {
          using std::cout;
          using namespace boost::filesystem;
        
          std::string path(argc <= 1 ? "." : argv[1]);
        
          if (is_directory(path)) {
            for (directory_iterator itr(path); itr!=directory_iterator(); ++itr) {
              cout << itr->path().filename() << ' '; // display filename only
              if (is_regular_file(itr->status())) // display size for regular files
                cout << " [" << file_size(itr->path()) << ']';
              cout << '\n';
            }
          }
          else cout << (exists(path) ? "Found: " : "Not found: ") << path << '\n';
        }
        

        【讨论】:

          【解决方案7】:

          选项 4. 使用 popen();

          【讨论】:

            【解决方案8】:

            共识似乎是不使用“ls”。但是,对于任何对执行此操作的功能感兴趣的人:

            /**
             * Execute a command and get the result.
             *
             * @param   cmd - The system command to run.
             * @return  The string command line output of the command.
             */
            string GetStdoutFromCommand(string cmd) {
            
                string data;
                FILE * stream;
                const int max_buffer = 256;
                char buffer[max_buffer];
                cmd.append(" 2>&1"); // Do we want STDERR?
            
                stream = popen(cmd.c_str(), "r");
                if (stream) {
                    while (!feof(stream))
                        if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer);
                    pclose(stream);
                }
                return data;
            }
            

            【讨论】:

              【解决方案9】:

              我认为 pajton 的#2 是对这个问题的最合适的答案。

              它可能是“ls”不是您想要使用它的命令的最佳示例,因为还有其他本机 C 库函数可以执行此操作,但是还有其他程序可以生成您可能想要的输出无需执行写入/读取文件即可立即处理。

              #1 也适用于 UNIX/Linux 类型系统,该系统实现了可用于读取/写入系统全局数据的高效 RAM 文件系统。在几乎所有系统上,这是完成工作的一种非常好的“快速”和“肮脏”方式。

              同样,大多数响应都提供了使用本地 C 库获取目录内容的更好方法,但在某些情况下,诸如 pipe()、fork()、dup()、exec()、system( ) 和 popen() 适合与系统进程通信。

              【讨论】:

                【解决方案10】:

                使用 fork()/exec() 和 pipe() 系统调用似乎是 Unix 上最好的通用方法。这允许最简单地分离被调用程序的标准输出和标准错误流。也可以等待产生的进程并收获它,收集它的退出状态。最后,如果需要,可以使用非阻塞 I/O 从被调用程序的输出/错误中读取。

                最大的问题是,要在 Windows 上实现类似的功能,您可能需要编写数百行代码。我没有找到更好的方法,也没有在 Windows 上下文中研究过这个问题。

                http://support.microsoft.com/kb/190351

                【讨论】:

                • 而 dup() 系统调用用于将子级中的输出重定向到管道的写入端。
                • Windows 上的差异很小。
                • 在 Windows 上,使用 CreatePipe 创建两个可继承的匿名管道 - 一个用于读取标准输出,一个用于您将调用的命令的标准错误。通过使用 CreateProcess 创建一个子进程来调用该命令,并将管道的写结束句柄传递给 STARTUPINFO 结构中的这个子进程。在程序中管道的读取端句柄上使用 ReadFile 以从 stdout 和 stderr 管道读取。最后,使用 WaitForSingleObject 等待子进程退出并在其进程句柄上调用 CloseHandle。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-07-16
                • 2012-07-03
                • 2014-07-01
                • 1970-01-01
                • 1970-01-01
                • 2020-10-24
                相关资源
                最近更新 更多