【发布时间】:2011-02-08 23:13:35
【问题描述】:
我正在用 C++ 编写一个程序,它对 Linux 操作系统上当前目录中的所有文件进行一些特殊处理。
所以我正在考虑使用诸如system("ls") 之类的系统调用来获取所有文件的列表。
但是如何将它存储在我的程序中? (如何将 ls 的输出重定向到我在程序中声明的字符串)
谢谢
【问题讨论】:
标签: c++ string redirect system call
我正在用 C++ 编写一个程序,它对 Linux 操作系统上当前目录中的所有文件进行一些特殊处理。
所以我正在考虑使用诸如system("ls") 之类的系统调用来获取所有文件的列表。
但是如何将它存储在我的程序中? (如何将 ls 的输出重定向到我在程序中声明的字符串)
谢谢
【问题讨论】:
标签: c++ string redirect system call
我建议你不要调用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
我认为您不能使用系统来读取输出。
尝试使用popen。
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
但是,您可能不想为ls 执行此操作。两个原因:
readdir),而不是使用 shell 命令并对其进行解析。ls 输出是一个坏主意。【讨论】:
ls 不起作用。
您使用的是 UNIX 还是 Windows?在 UNIX 上,您将使用 fork() 调用创建一个子进程,然后创建一些管道并将子进程的 STDOUT 分配给父进程的 STDIN。在 Windows 上,Win32 API 中肯定会有类似的东西。
【讨论】:
我看到了 3 个选项:
system("ls > tempfile"),然后从临时文件中读取ls 的输出pipe() 打开一个管道,然后使用fork() 生成一个新进程。在子进程中关闭文件描述符 2 并通过调用dup() 替换它的写入管道结束。接下来,在子进程中调用exec("ls",...)。最后从父进程的管道中读取ls的输出ls 枚举当前目录中的文件。您的程序中有一些功能可以做到这一点,即opendir(),readdir(),closedir。我强烈推荐选项 3。
【讨论】:
要么使用适当的 c 调用来读取目录中的文件,要么概括您的程序,以便它将文件列表作为输入并将该列表从 ls 管道传输到您的程序
> ls | yoursoftware
【讨论】:
一个简单的方法是使用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';
}
【讨论】:
选项 4. 使用 popen();
【讨论】:
共识似乎是不使用“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;
}
【讨论】:
我认为 pajton 的#2 是对这个问题的最合适的答案。
它可能是“ls”不是您想要使用它的命令的最佳示例,因为还有其他本机 C 库函数可以执行此操作,但是还有其他程序可以生成您可能想要的输出无需执行写入/读取文件即可立即处理。
#1 也适用于 UNIX/Linux 类型系统,该系统实现了可用于读取/写入系统全局数据的高效 RAM 文件系统。在几乎所有系统上,这是完成工作的一种非常好的“快速”和“肮脏”方式。
同样,大多数响应都提供了使用本地 C 库获取目录内容的更好方法,但在某些情况下,诸如 pipe()、fork()、dup()、exec()、system( ) 和 popen() 适合与系统进程通信。
【讨论】:
使用 fork()/exec() 和 pipe() 系统调用似乎是 Unix 上最好的通用方法。这允许最简单地分离被调用程序的标准输出和标准错误流。也可以等待产生的进程并收获它,收集它的退出状态。最后,如果需要,可以使用非阻塞 I/O 从被调用程序的输出/错误中读取。
最大的问题是,要在 Windows 上实现类似的功能,您可能需要编写数百行代码。我没有找到更好的方法,也没有在 Windows 上下文中研究过这个问题。
【讨论】: