我需要走一个目录树并获取每个文件的stat值.我想在修改文件系统时安全地执行此操作.
在Python中,最好的选项是os.fwalk
,它允许访问被遍历目录的fd;然后我可以使用dir_fd(fstatat)os.stat并获取当前的stat值.这是可以在Linux上进行的无竞争(如果正在修改此目录的内容,我可能需要重新扫描它).在C中,有nftw
,它是类似地实现的,而fts
,在glibc中使用plain(l)stat,因此是racy(它通过改变目录来减少竞争窗口,这是不方便的).
C有一个新的filesystem API graduated from boost,它缓存了stat值但是doesn’t expose them(我需要访问st_dev).这不仅仅是一个头库,所以我无法解决这个问题.
我错过了一个不错的C选项,它使用fstatat并且不受Boost理想的不暴露特定于平台的调用的约束?或者是我最好的选择包裹nftw(甚至找到)?
最佳答案 事实证明它很容易实现.
我从干项目中使用了libposix.
#include <posix++.h>
class Walker {
public:
void walk(posix::directory dir) {
dir.for_each([this, dir](auto& dirent) {
if (dirent.name == "." or dirent.name == "..")
return;
if (!handle_dirent(dirent))
return;
struct stat stat;
if (dirent.type == DT_DIR || dirent.type == DT_UNKNOWN) {
int fd = openat(
dir.fd(), dirent.name.c_str(), O_DIRECTORY|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
// ELOOP when O_NOFOLLOW is used on a symlink
if (errno == ENOTDIR || errno == ELOOP)
goto enotdir;
if (errno == ENOENT)
goto enoent;
posix::throw_error(
"openat", "%d, \"%s\"", dir.fd(), dirent.name);
}
posix::directory dir1(fd);
fstat(fd, &stat);
if (handle_directory(dirent, fd, stat))
walk(dir1);
close(fd);
return;
}
enotdir:
try {
dir.stat(dirent.name.c_str(), stat, AT_SYMLINK_NOFOLLOW);
} catch (const posix::runtime_error &error) {
if (error.number() == ENOENT)
goto enoent;
throw;
}
handle_file(dirent, stat);
return;
enoent:
handle_missing(dirent);
});
}
protected:
/* return value: whether to stat */
virtual bool handle_dirent(const posix::directory::entry&) { return true; }
/* return value: whether to recurse
* stat will refer to a directory, dirent info may be obsolete */
virtual bool handle_directory(
const posix::directory::entry &dirent,
const int fd, const struct stat&) { return true; }
/* stat might refer to a directory in case of a race;
* it still won't be recursed into. dirent may be obsolete. */
virtual void handle_file(
const posix::directory::entry &dirent,
const struct stat&) {}
/* in case of a race */
virtual void handle_missing(
const posix::directory::entry &dirent) {}
};
性能与GNU find相同(当与基类进行比较时,使用-size $RANDOM来抑制输出并强制查找统计所有文件,而不仅仅是DT_DIR候选者).