我正在尝试用C++编写一个跨平台的globbing方法。为此,我尝试使用std::filesystem
库递归遍历目录,并将路径与提供的路径正则表达式进行比较。
我尝试使用的代码是:
std::vector<std::string> glob(const std::string& regexPattern) {
std::vector<std::string> matches
fs::path currentPath = fs::current_path();
fs::path dirPath = regexPattern;
if (dirPath.is_relative()) {
dirPath = currentPath / dirPath;
}
size_t pos = 0;
std::string path_upto_wildcard = "/";
std::string str_dirPath = dirPath.string();
while ((pos = str_dirPath.find("/")) != std::string::npos) {
std::string token =str_dirPath.substr(0, pos);
str_dirPath.erase(0, pos + 1);
if (token == "" ) continue;
cleanPattern += token + "/";
path_upto_wildcard += token + "/";
}
dirPath = path_upto_wildcard;
std::regex regEx(regexPattern);
fs::recursive_directory_iterator endIterator;
for (fs::recursive_directory_iterator it(dirPath); it != endIterator; ++it) {
if ( std::regex_match(it->path().string(), regEx)) {
matches.push_back(it->path().string());
}
}
return matches;
}
当我编译并运行使用这个方法的可执行文件时,它运行得很好。但是,我在一个可执行文件中使用了这段代码,我希望在同一台机器上在后台并发运行100次(使用不同的参数)。当我这样做的时候,我最终会看到一些进程完成得很好,但其他许多进程会抛出以下错误:
terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
what(): filesystem error: cannot increment recursive directory iterator: No such file or directory
(core dumped)
如果我使用std::filesystem
或boost::filesystem
,就会发生这种情况。但是,如果我编写这个glob方法来使用依赖于unix的glob.hpp
库,那么一切都能按预期运行,没有任何问题。glob.hpp
代码:
std::vector<std::string> glob(const std::string& pattern) {
glob_t g;
glob(pattern.c_str(), GLOB_TILDE, nullptr, &g); // one should ensure glob returns 0!
std::vector<std::string> filelist;
filelist.reserve(g.gl_pathc);
for (size_t i = 0; i < g.gl_pathc; ++i) {
filelist.emplace_back(g.gl_pathv[i]);
}
globfree(&g);
return filelist;
}
我不确定不稳定性来自哪里,如果有什么我可以做的呢?
下面是一个最小的工作示例:
#include <regex>
#include <filesystem>
#include <glob.h>
#include <iostream>
namespace fs = std::filesystem;
std::vector<std::string> glob(const std::string& regexPattern) {
std::vector<std::string> matches;
fs::path currentPath = fs::current_path();
fs::path dirPath = regexPattern;
if (dirPath.is_relative()) {
dirPath = currentPath / dirPath;
}
size_t pos = 0;
std::string path_upto_wildcard = "/";
std::string str_dirPath = dirPath.string();
while ((pos = str_dirPath.find("/")) != std::string::npos) {
std::string token =str_dirPath.substr(0, pos);
str_dirPath.erase(0, pos + 1);
if (token == "" ) continue;
if (token.find("*") != std::string::npos) continue;
path_upto_wildcard += token + "/";
}
dirPath = path_upto_wildcard;
std::regex regEx(regexPattern);
fs::recursive_directory_iterator endIterator;
for (fs::recursive_directory_iterator it(dirPath); it != endIterator; ++it) {
if ( std::regex_match(it->path().string(), regEx)) {
matches.push_back(it->path().string());
}
}
return matches;
}
int main() {
std::vector dirs{"/mypath/a/.*XYZ*/.*",
"/mypath/b/.*XYZ*/.*",
"/mypath/c/.*XYZ*/.*",
};
for (auto& dir : dirs) {
std::vector<std::string> matches = glob(dir);
for (auto& match : matches) {
std::cout << match << std::endl;
}
}
return 0;
}
它可以用
g++ test_globbing.cxx -o test_globbing -std=c++17
并且可以通过创建一些文本文件test_glob.txt
来测试效果,该文本文件包含
./test_globbing
100次(每行一次),然后运行run_in_bkg.sh
文件,其中包含:
#!/bin/bash/
n=0
while read arg; do echo $n && ((n+=1)); eval ' $arg &> job_$(echo $n).log &'; sleep .2; done < $1
这种影响并不总是存在,我也不能让它“总是”发生,但是随着正在运行的进程数量的增加,每运行几次,您就会看到一些作业失败。
1条答案
按热度按时间42fyovps1#
不幸的是,该标准没有指定并发文件系统操作如何工作,特别是它使文件系统竞争未定义的行为。
幸运的是,还有另一种递增目录迭代器的方法:
std::filesystem::recursive_directory_iterator::increment
。它接受一个
std::error_code
参数并设置它,而不是抛出一个文件系统异常。所以,我相信你可以这样写,而不是你的for循环:我不能重现你的问题,即使你最小的工作示例(我试图翻译到Windows作为最好的,我可以),所以我不保证什么。我也不确定如果
it.increment
失败了,它是否还会递增it
,所以你可能需要像这样复制迭代器: