网站首页 > linux / 正文
我们来深入了解 filesystem目录相关部分。
1. 创建
创建目录注意:
a. 目录的父目录是否存在
b. 目录已经存在处理
1.1 函数
bool create_directory( const std::filesystem::path& p ); | (1) |
bool create_directory( const std::filesystem::path& p, std::error_code& ec ) noexcept; | (2) |
bool create_directory( const std::filesystem::path& p, const std::filesystem::path& existing_p ); | (3) |
bool create_directory( const std::filesystem::path& p, const std::filesystem::path& existing_p, std::error_code& ec ) noexcept; | (4) |
bool create_directories( const std::filesystem::path& p ); | (5) |
bool create_directories( const std::filesystem::path& p, std::error_code& ec ); | (6) |
1,2) 如同用 POSIX mkdir() 以 static_cast<int>(std::filesystem::perms::all) 为第二实参来创建目录 p(父目录必须已经存在)。若该函数因为 p 解析到既存目录而失败,则不报告错误。否则在失败时报告错误。
2) 同 (1,2),但新目录的属性复制自 existing_p(必须是已存在的目录)。复制的属性取决于操作系统:在 POSIX 系统上,如同按照下方复制属性
stat(existing_p.c_str(), &attributes_stat)
mkdir(p.c_str(), attributes_stat.st_mode)
在 Windows 操作系统上,不复制 existing_p 的属性。
5,6) 对每个尚未存在的 p 的元素执行 (1,2)。若 p 已存在,则函数不做任何事(不把此条件当做错误)。
参数
p | 要创建的新目录的路径 |
existing_p | 要自之复制属性的目录的路径 |
ec | 不抛出重载中报告错误的输出形参 |
返回值
若创建了 p 所解析到的目录则为 true,否则为 false。
异常
若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。
1,5) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。
若 OS API 调用失败,则 @2,6@ 设置 std::error_code& 形参
为 OS API 错误码,而未发生错误时则执行 ec.clear()。
3) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参,以 existing_p 为第二路径实参,并以OS 错误码为错误码实参。
若 OS API 调用失败,则 @4@ 设置 std::error_code& 形参
为 OS API 错误码,而未发生错误时则执行 ec.clear()。
注解
保持属性的重载 (3,4) 被 copy() 在递归地复制目录时隐式调用。其在 boost.filesystem 的等价物是 copy_directory(实参顺序相反)。
1. 2 示例
#include <iostream>
#include <string>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
//获取当前路径
fs::path currentPath = fs::current_path();
std::error_code ec;
bool ret = false;
//创建 tdir/sub 目录
//create_directory
//创建因为要求父目录必须已经存在,
//而(tdir不存在)所以下面必定失败
fs::path path(currentPath);
path.append("tdir/sub_tdir");
std::cout << path << std::endl;
try {
//失败,抛异常,所以要处理
fs::create_directory(path);
}
catch (fs::filesystem_error& e) {
std::cout << "failed ";
std::cout << e.code() << " : " << e.what() << std::endl;
}
//失败不抛异常
if (!fs::create_directory(path, ec) && ec) {
std::cout << "failed ";
std::cout <<ec.value()<<" : " << ec.message() << std::endl;
}
//create_directories
//上级目录不存在,则创建
if (!fs::create_directories(path, ec) && ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
return 0;
}
std::cout << "ok" << std::endl;
//目录已经存在,再创建, 会返回失败
//这时需要判断ec
if (!fs::create_directory(path, ec) && !ec) {
std::cout << "目录已经存在" << std::endl;
}
if (!fs::create_directories(path, ec) && !ec) {
std::cout << "目录已经存在" << std::endl;
}
std::cout << "done" << std::endl;
//综上所述:
//1、尽量不要使用抛异常函数
//2、目录存在,函数返回false,
// 要判断std::error_code
return 0;
}
1.3 平台API
平台创建目录的API Windows
#include <windows.h>
BOOL
WINAPI
CreateDirectoryW(
_In_ LPCWSTR lpPathName,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
Linux
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
2. 删除
删除目录注意:
a. 非空目录怎样删除
2.1 函数
bool remove( const std::filesystem::path& p ); | (1) |
bool remove( const std::filesystem::path& p, std::error_code& ec ) noexcept; | (2) |
std::uintmax_t remove_all( const std::filesystem::path& p ); | (3) |
std::uintmax_t remove_all( const std::filesystem::path& p, std::error_code& ec ); | (4) |
1,2) 删除路径 p 所标识的文件或空目录,如同用 POSIX remove。不跟随符号链接(移除符号链接,而非其目标)。
3,4) 递归地删除 p 的内容(若它是目录)及其所有子目录的内容,然后删除 p 自身,如同重复应用 POSIX remove。不跟随符号链接(移除符号链接,而非其目标)。
参数
p | 要删除的路径 |
ec | 不抛出重载中报告错误的输出形参 |
返回值
1,2) 若文件被删除则为 true,若文件不存在则为 false。接受 error_code& 实参的重载在错误时返回 false。
3,4) 返回被删除的文件及目录数量(可以是零,若用以起始的 p 不存在)。接受 error_code& 实参的重载在错误时返回 static_caststd::uintmax_t(-1)。
异常
若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。
1,3) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。
若 OS API 调用失败,则 @2,4@ 设置 std::error_code& 形参
为 OS API 错误码,而未发生错误时则执行 ec.clear()。
注解
在 POSIX 系统上,此函数通常按需调用 unlink 和 rmdir,在 Windows 上则是 RemoveDirectoryW 和 DeleteFileW。
2.2 示例
#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
//获取当前路径
fs::path currentPath = fs::current_path();
std::error_code ec;
bool ret = false;
//创建 tdir/sub 目录
fs::path path(currentPath);
path.append("tdir/sub_tdir");
if (!fs::create_directories(path, ec) && ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
return 0;
}
//创建 0.txt ... 9.txt 文件
for (int i = 0; i < 10; i++) {
auto fPath(path);
fPath.append(std::to_string(i) + ".txt");
std::ofstream fp(fPath.generic_string(), std::ios::out);
fp.write("hello world", 11);
fp.close();
}
//remove 只能删除空目录,
//所以这里失败
if (!fs::remove(path, ec) && ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
}
//remove_all 删除目录及其下内容
//所以这里会成功
path = path.parent_path();//上级目录
int files = fs::remove_all(path, ec);
if (ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
return 0;
}
std::cout << "ok remove files : " << files << std::endl;
//综上所述:
//1、尽量不要使用抛异常函数
//2、删除非空目录,使用remove_all
return 0;
}
3. 属性与权限
3.1 函数
- 获取属性权限
std::filesystem::file_status status( const std::filesystem::path& p ); | (1) |
std::filesystem::file_status status( const std::filesystem::path& p, std::error_code& ec ) noexcept; | (2) |
std::filesystem::file_status symlink_status( const std::filesystem::path& p ); | (3) |
std::filesystem::file_status symlink_status( const std::filesystem::path& p, std::error_code& ec ) noexcept; | (4) |
1,2) 确定 p 所标识的文件系统对象的类型与属性,如同用 POSIX stat(符号链接跟随到其目标)。在下列描述中,prms 是 (m & perms::mask) 的结果,其中 m 如同通过从 POSIX struct stat 采用 st_mode 获得,并将其转换为类型 std::filesystem::perms。
3,4) 同 (1,2),但行为如同使用 POSIX lstat(不跟随符号链接):
参数
p | 要检验的路径 |
ec | 不抛出重载中报告错误的输出形参 |
返回值
文件状态(一个 filesystem::file_status 对象)
异常
若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。
1,3) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。
若 OS API 调用失败,则 @2,4@ 设置 std::error_code& 形参
为 OS API 错误码,而未发生错误时则执行 ec.clear()。
注解
此函数提供的信息通常也作为目录迭代的副产物提供,而且可能为 filesystem::directory_entry 的成员函数所获取。在目录迭代期间,不需要再次调用 status。
- 修改权限
void permissions( const std::filesystem::path& p, std::filesystem::perms prms, std::filesystem::perm_options opts = perm_options::replace ); | (1) |
void permissions( const std::filesystem::path& p, std::filesystem::perms prms, std::error_code& ec ) noexcept; | (2) |
void permissions( const std::filesystem::path& p, std::filesystem::perms prms, std::filesystem::perm_options opts, std::error_code& ec ); | (3) |
更改 p 所解析的文件的访问权限,如同用 POSIX fchmodat。跟随符号链接,除非在 opts 中设置了 perm_options::nofollow。
第二个签名表现如同以设为 perm_options::replace 的 opts 调用。
效果按如下方式依赖于 prms 与 opts:
- 若 opts 是 perm_options::replace,则严格设置文件权限为 prms & std::filesystem::perms::mask 表示应用 prms 的每个有效位)。
- 若 opts 是 perm_options::add,则严格设置文件权限为 status(p).permissions() | (prms & perms::mask)(表示每个设于 prms 却不在当前文件权限中的有效位被添加到文件权限)。
- 若 opts 是 perm_options::remove,则严格设置文件权限为 status(p).permissions() & ~(prms & perms::mask)(表示每个 prms 中清除却设于当前文件权限中的有效位从文件权限被清除)。
opts 要求 replace、add 或 remove 中有且仅有一者被设置。
不抛出重载在错误时无特殊行动。
参数
p | 要检验的路径 |
prms | 要设置、添加或移除的权限 |
opts | 控制此函数所采用行动的选项 |
ec | 不抛出重载中报告错误的输出形参 |
异常
若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。
1) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。
若 OS API 调用失败,则 @2,3@ 设置 std::error_code& 形参
为 OS API 错误码,而未发生错误时则执行 ec.clear()。
注解 权限不必用位实现,但概念上用这种方式处理它们。 某些系统上某些权限位可能被忽略,而更改某些位可能自动影响其他位(例如在无所有者/组/全体区分的平台上,设置三者任一部分都会写入三者全体)。
3.2 示例
#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
//获取当前路径
fs::path currentPath = fs::current_path();
std::error_code ec;
bool ret = false;
//创建 tdir/sub 目录
fs::path path(currentPath);
path.append("tdir/sub_tdir");
if (!fs::create_directories(path, ec) && ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
return 0;
}
std::cout << path << std::endl;
fs::file_status st = fs::status(path, ec);
if (ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
return 0;
}
//类型
//if(fs::is_directory(path))
if (fs::file_type::directory == st.type()) {
std::cout << "确实是目录" << std::endl;
}
//打印权限
auto show = [](fs::perms ps) {
std::cout << (fs::perms::none == (fs::perms::owner_read & ps) ? '-' : 'r');
std::cout << (fs::perms::none == (fs::perms::owner_write & ps) ? '-' : 'w');
std::cout << (fs::perms::none == (fs::perms::owner_exec & ps) ? '-' : 'x');
std::cout << (fs::perms::none == (fs::perms::group_read & ps) ? '-' : 'r');
std::cout << (fs::perms::none == (fs::perms::group_write & ps) ? '-' : 'w');
std::cout << (fs::perms::none == (fs::perms::group_exec & ps) ? '-' : 'x');
std::cout << (fs::perms::none == (fs::perms::others_read & ps) ? '-' : 'r');
std::cout << (fs::perms::none == (fs::perms::others_write & ps) ? '-' : 'w');
std::cout << (fs::perms::none == (fs::perms::others_exec & ps) ? '-' : 'x');
std::cout << '\n';
};
//权限
fs::perms ps = st.permissions();
show(ps);
//修改权限: 非所有者无权限
ps = fs::perms::owner_all;
if (fs::permissions(path, ps, fs::perm_options::replace, ec), ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
return 0;
}
ps = fs::status(path, ec).permissions();
show(ps);
//remove_all 删除目录及其下内容
//所以这里会成功
path = path.parent_path();//上级目录
int files = fs::remove_all(path, ec);
if (ec) {
std::cout << "failed ";
std::cout << ec.value() << " : " << ec.message() << std::endl;
return 0;
}
std::cout << "ok remove files : " << files << std::endl;
//综上所述:
//1、尽量不要使用抛异常函数
return 0;
}
4. 遍历
- 遍历目录
4.1 类
class directory_iterator | 目录元素迭代器 |
class recursive_directory_iterator | 递归目录元素迭代器 |
- directory_iterator
- 遍历目录元素directory_entry。 不遍历子目录。 迭代顺序是未指定的,但每个目录条目只被造访一次。 跳过特殊路径名 . 和 ..
- class recursive_directory_iterator 遍历目录元素directory_entry,迭代顺序是未指定的,但每个目录条目只被造访一次。 遍历子目录。 迭代顺序是未指定的,但每个目录条目只被造访一次。 跳过特殊路径名 . 和 ..
4.2 示例
#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
//获取当前路径
fs::path currentPath = fs::current_path();
std::error_code ec;
bool ret = false;
//打印权限
auto show = [](fs::perms ps) {
std::cout << " ";
std::cout << (fs::perms::none == (fs::perms::owner_read & ps) ? '-' : 'r');
std::cout << (fs::perms::none == (fs::perms::owner_write & ps) ? '-' : 'w');
std::cout << (fs::perms::none == (fs::perms::owner_exec & ps) ? '-' : 'x');
std::cout << (fs::perms::none == (fs::perms::group_read & ps) ? '-' : 'r');
std::cout << (fs::perms::none == (fs::perms::group_write & ps) ? '-' : 'w');
std::cout << (fs::perms::none == (fs::perms::group_exec & ps) ? '-' : 'x');
std::cout << (fs::perms::none == (fs::perms::others_read & ps) ? '-' : 'r');
std::cout << (fs::perms::none == (fs::perms::others_write & ps) ? '-' : 'w');
std::cout << (fs::perms::none == (fs::perms::others_exec & ps) ? '-' : 'x');
};
fs::path dir;
//directory_iterator
std::cout << "directory_iterator" << std::endl;
for (const auto& entry : fs::directory_iterator(currentPath, ec)) {
if (dir != entry.path().parent_path()) {
dir = entry.path().parent_path();
std::cout << "\n" << dir << ":" << std::endl;
}
std::cout << (entry.is_directory() ? "DIR" : "FILE");
std::cout << " " << entry.path().filename();
std::cout << " " << entry.file_size();
show(entry.status().permissions());
std::cout << std::endl;
}
//recursive_directory_iterator
std::cout << "recursive_directory_iterator" << std::endl;
for (const auto& entry : fs::recursive_directory_iterator(currentPath, ec)) {
if (dir != entry.path().parent_path()) {
dir = entry.path().parent_path();
std::cout << "\n" << dir << ":" << std::endl;
}
std::cout << (entry.is_directory() ? "DIR" : "FILE");
std::cout << " " << entry.path().filename();
std::cout << " " << entry.file_size();
show(entry.status().permissions());
std::cout << std::endl;
}
//综上所述:
//1、尽量不要使用抛异常函数
return 0;
}
4.3 解析
for (const auto& entry : fs::directory_iterator(currentPath, ec)) {...}
上面的遍历目录代码有些难解, 我们来解析一下。
我们知道上面的语句在编译时会转化成下面类似的语句
{
auto && __range = range-expression ;
auto __begin = begin-expr ;
auto __end = end-expr ;
for ( ; __begin != __end; ++__begin)
{
range-declaration = *__begin;
loop-statement
}
}
下面我们就看directory_iterator源码(有删减)
按上面的转化, 大约可得:
{
auto && __range = range-expression ;
auto __begin = begin(directory_iter(path, ec)) ;
auto __end = end(directory_iter()) ;
for ( ; __begin != __end; ++__begin)
{
entry = *__begin;//const directory_entry& operator*()
loop-statement
}
}
Tags:linux 上级目录
- 上一篇:Linux 各目录详细介绍
- 下一篇:Linux(二)常用目录结构
猜你喜欢
- 2024-11-26 Linux 文件与目录管理(杰哥教你Linux)
- 2024-11-26 Linux学习总结4
- 2024-11-26 Linux CentOS 7 目录结构及特点(建议收藏)
- 2024-11-26 Linux目录介绍
- 2024-11-26 Linux常用指令简介(2):文件和目录管理
- 2024-11-26 rhcsa8精品课笔记-2之linux目录结构
- 2024-11-26 Linux常用工作目录切换命令
- 2024-11-26 linux学习之系统目录
- 2024-11-26 基于CentOS8Linux运维教程-Linux文件目录管理笔记
- 2024-11-26 Linux 文件与目录管理——想玩转linux就请一直看下去