如何在檔案系統上搜索與檔案描述符關聯的所有硬鏈接?
uj5u.com熱心網友回復:
這里有一個更特定于平臺的方法:
https://stackoverflow.com/a/70728234/4821390
如果您不能使用 std::filesystem,則需要 C 17 或使用 -DUSE_GHC_FILESYSTEM 編譯的 ghc::filesystem 的頭檔案。ghc::filesystem 這里:
https://github.com/gulrak/filesystem
測驗了 Windows、macOS、Linux、FreeBSD、DragonFly BSD、OpenBSD 和 Emscripten。可能支持 NetBSD、Illumos、Android、iOS 和其他 POSIX 兼容。
findhardlinks.hpp:
#include <string>
#include <vector>
#if !defined(USE_GHC_FILESYSTEM)
#include <filesystem>
#endif
#include <cstdlib>
#include <cstring>
#if defined(_WIN32)
#include <cwchar>
#endif
#if defined(USE_GHC_FILESYSTEM)
#include "filesystem.hpp"
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(_WIN32)
#include <windows.h>
#include <share.h>
#include <io.h>
#else
#include <unistd.h>
#endif
namespace findhardlinks {
#if defined(USE_GHC_FILESYSTEM)
namespace fs = ghc::filesystem;
#else
namespace fs = std::filesystem;
#endif
namespace {
/* necessary in GUI Windows applications to process window
clicks without crashing during lasting for/while loops. */
inline void message_pump() {
#if defined(_WIN32)
MSG msg; while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#endif
}
#if defined(_WIN32)
// UTF-8 support on Windows: string to wstring.
inline std::wstring widen(std::string str) {
std::size_t wchar_count = str.size() 1; std::vector<wchar_t> buf(wchar_count);
return std::wstring{ buf.data(), (std::size_t)MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buf.data(), (int)wchar_count) };
}
// UTF-8 support on Windows: wstring to string.
inline std::string narrow(std::wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), nullptr, 0, nullptr, nullptr); std::vector<char> buf(nbytes);
return std::string{ buf.data(), (std::size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, nullptr, nullptr) };
}
#endif
// optional: get environment variable value.
inline std::string environment_get_variable(std::string name) {
#if defined(_WIN32)
std::string value;
wchar_t buffer[32767];
std::wstring u8name = widen(name);
if (GetEnvironmentVariableW(u8name.c_str(), buffer, 32767) != 0) {
value = narrow(buffer);
}
return value;
#else
char *value = getenv(name.c_str());
return value ? value : "";
#endif
}
// optional: check if environment variable exists.
inline bool environment_get_variable_exists(std::string name) {
#if defined(_WIN32)
std::string value;
wchar_t buffer[32767];
std::wstring u8name = widen(name);
GetEnvironmentVariableW(u8name.c_str(), buffer, 32767);
return (GetLastError() != ERROR_ENVVAR_NOT_FOUND);
#else
return (getenv(name.c_str()) != nullptr);
#endif
}
// optional: expand ${ENVVAR} in strings.
inline std::string environment_expand_variables(std::string str) {
if (str.find("${") == std::string::npos) return str;
std::string pre = str.substr(0, str.find("${"));
std::string post = str.substr(str.find("${") 2);
if (post.find('}') == std::string::npos) return str;
std::string variable = post.substr(0, post.find('}'));
std::size_t pos = post.find('}') 1; post = post.substr(pos);
std::string value = environment_get_variable(variable);
if (!environment_get_variable_exists(variable))
return str.substr(0, pos) environment_expand_variables(str.substr(pos));
return environment_expand_variables(pre value post);
}
/* force absolute path and remove trailing slashes;
keep trailing slash at the end if drive/fs root. */
inline std::string expand_without_trailing_slash(std::string dname) {
std::error_code ec;
dname = environment_expand_variables(dname);
fs::path p = fs::path(dname);
p = fs::absolute(p, ec);
if (ec.value() != 0) return "";
dname = p.string();
#if defined(_WIN32)
while ((dname.back() == '\\' || dname.back() == '/') &&
(p.root_name().string() "\\" != dname && p.root_name().string() "/" != dname)) {
message_pump(); p = fs::path(dname); dname.pop_back();
}
#else
while (dname.back() == '/' && (!dname.empty() && dname[0] != '/' && dname.length() != 1)) {
dname.pop_back();
}
#endif
return dname;
}
// force absolute path and add trailing slash.
inline std::string expand_with_trailing_slash(std::string dname) {
dname = expand_without_trailing_slash(dname);
#if defined(_WIN32)
if (dname.back() != '\\') dname = "\\";
#else
if (dname.back() != '/') dname = "/";
#endif
return dname;
}
// check if regular file (non-directory) exists.
inline bool file_exists(std::string fname) {
std::error_code ec;
fname = expand_without_trailing_slash(fname);
const fs::path path = fs::path(fname);
return (fs::exists(path, ec) && ec.value() == 0 &&
(!fs::is_directory(path, ec)) && ec.value() == 0);
}
// check if directory exists.
inline bool directory_exists(std::string dname) {
std::error_code ec;
dname = expand_without_trailing_slash(dname);
dname = expand_without_trailing_slash(dname);
const fs::path path = fs::path(dname);
return (fs::exists(path, ec) && ec.value() == 0 &&
fs::is_directory(path, ec) && ec.value() == 0);
}
// convert relative path to absolute path, when applicable.
inline std::string filename_absolute(std::string fname) {
std::string result;
if (directory_exists(fname)) {
result = expand_with_trailing_slash(fname);
} else if (file_exists(fname)) {
result = expand_without_trailing_slash(fname);
}
return result;
}
// find hardlinks helper struct.
struct findhardlinks_struct {
std::vector<std::string> x;
std::vector<std::string> y;
bool recursive;
unsigned i;
unsigned j;
#if defined(_WIN32)
BY_HANDLE_FILE_INFORMATION info;
#else
struct stat info;
#endif
};
/* find hardlinks directory iterator: search for equal files
like std::filesystem::equivalent but passing fd not path. */
std::vector<std::string> findhardlinks_result;
inline void findhardlinks_helper(findhardlinks_struct *s) {
#if defined(_WIN32)
if (findhardlinks_result.size() >= s->info.nNumberOfLinks) return;
#else
if (findhardlinks_result.size() >= s->info.st_nlink) return;
#endif
if (s->i < s->x.size()) {
std::error_code ec; if (!directory_exists(s->x[s->i])) return;
s->x[s->i] = expand_without_trailing_slash(s->x[s->i]);
const fs::path path = fs::path(s->x[s->i]);
if (directory_exists(s->x[s->i]) || path.root_name().string() "\\" == path.string()) {
fs::directory_iterator end_itr;
for (fs::directory_iterator dir_ite(path, ec); dir_ite != end_itr; dir_ite.increment(ec)) {
message_pump(); if (ec.value() != 0) { break; }
fs::path file_path = fs::path(filename_absolute(dir_ite->path().string()));
#if defined(_WIN32)
int fd = -1;
BY_HANDLE_FILE_INFORMATION info = { 0 };
if (file_exists(file_path.string())) {
// printf("%s\n", file_path.string().c_str());
if (!_wsopen_s(&fd, file_path.wstring().c_str(), _O_RDONLY, _SH_DENYNO, _S_IREAD)) {
bool success = GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info);
bool matches = (info.ftLastWriteTime.dwLowDateTime == s->info.ftLastWriteTime.dwLowDateTime &&
info.ftLastWriteTime.dwHighDateTime == s->info.ftLastWriteTime.dwHighDateTime &&
info.nFileSizeHigh == s->info.nFileSizeHigh && info.nFileSizeLow == s->info.nFileSizeLow &&
info.nFileSizeHigh == s->info.nFileSizeHigh && info.nFileSizeLow == s->info.nFileSizeLow &&
info.dwVolumeSerialNumber == s->info.dwVolumeSerialNumber);
if (matches && success) {
findhardlinks_result.push_back(file_path.string());
if (findhardlinks_result.size() >= info.nNumberOfLinks) {
s->info.nNumberOfLinks = info.nNumberOfLinks; s->x.clear();
_close(fd);
return;
}
}
_close(fd);
}
}
#else
struct stat info = { 0 };
if (file_exists(file_path.string())) {
// printf("%s\n", file_path.string().c_str());
if (!stat(file_path.string().c_str(), &info)) {
if (info.st_dev == s->info.st_dev && info.st_ino == s->info.st_ino &&
info.st_size == s->info.st_size && info.st_mtime == s->info.st_mtime) {
findhardlinks_result.push_back(file_path.string());
if (findhardlinks_result.size() >= info.st_nlink) {
s->info.st_nlink = info.st_nlink; s->x.clear();
return;
}
}
}
}
#endif
if (s->recursive && directory_exists(file_path.string())) {
// printf("%s\n", file_path.string().c_str());
s->x.push_back(file_path.string());
s->i ; findhardlinks_helper(s);
}
}
}
}
while (s->j < s->y.size() && directory_exists(s->y[s->j])) {
message_pump(); s->x.clear(); s->x.push_back(s->y[s->j]);
s->j ; findhardlinks_helper(s);
}
}
} // anonymous namespace
// the actual function to call.
inline std::vector<std::string> findhardlinks(int fd, std::vector<std::string> dnames, bool recursive) {
std::vector<std::string> paths;
#if defined(_WIN32)
BY_HANDLE_FILE_INFORMATION info = { 0 };
if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info) && info.nNumberOfLinks) {
#else
struct stat info = { 0 };
if (!fstat(fd, &info) && info.st_nlink) {
#endif
findhardlinks_result.clear();
struct findhardlinks_struct s;
std::vector<std::string> first;
first.push_back(dnames[0]);
dnames.erase(dnames.begin());
s.x = first;
s.y = dnames;
s.i = 0;
s.j = 0;
s.recursive = recursive;
s.info = info;
findhardlinks_helper(&s);
paths = findhardlinks_result;
}
return paths;
}
} // namespace findhardlinks
例子.cpp:
#include <cstdio>
#include <algorithm>
#include "findhardlinks.hpp"
#if defined(_WIN32)
#include <cwchar>
#endif
using std::string;
using std::vector;
using std::size_t;
#if defined(_WIN32)
using std::wstring;
#endif
namespace {
/* read, write, append
open() permissions. */
enum {
FD_RDONLY,
FD_WRONLY,
FD_RDWR,
FD_APPEND,
FD_RDAP
};
#if defined(_WIN32)
// UTF-8 support on Windows: string to wstring.
wstring widen(string str) {
size_t wchar_count = str.size() 1; vector<wchar_t> buf(wchar_count);
return wstring{ buf.data(), (size_t)MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buf.data(), (int)wchar_count) };
}
// UTF-8 support on Windows: wstring to string.
string narrow(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), nullptr, 0, nullptr, nullptr); vector<char> buf(nbytes);
return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, nullptr, nullptr) };
}
#endif
// replace all substrings with new substring in string.
string string_replace_all(string str, string substr, string nstr) {
size_t pos = 0;
while ((pos = str.find(substr, pos)) != string::npos) {
str.replace(pos, substr.length(), nstr);
pos = nstr.length();
}
return str;
}
// path without filename spec.
string filename_path(string fname) {
#if defined(_WIN32)
size_t fp = fname.find_last_of("\\/");
#else
size_t fp = fname.find_last_of("/");
#endif
if (fp == string::npos) return fname;
return fname.substr(0, fp 1);
}
// path without directory path spec.
string filename_name(string fname) {
#if defined(_WIN32)
size_t fp = fname.find_last_of("\\/");
#else
size_t fp = fname.find_last_of("/");
#endif
if (fp == string::npos) return fname;
return fname.substr(fp 1);
}
// get temporary directory.
string directory_get_temporary_path() {
std::error_code ec;
string result = findhardlinks::fs::temp_directory_path(ec).string();
if (result.back() != '/') result.push_back('/');
#if defined(_WIN32)
result = string_replace_all(result, "/", "\\");
#endif
return (ec.value() == 0) ? result : "";
}
// write string to file descriptor.
long file_write_string(int fd, string str) {
char *buffer = str.data();
#if defined(_WIN32)
long result = _write(fd, buffer, (unsigned)str.length());
#else
long result = write(fd, buffer, (unsigned)str.length());
#endif
return result;
}
/* create a temporary file with the string contents str.
filename suffix - replaces XXXXXX in buffer randomly. */
int file_open_from_string(string str) {
string fname = directory_get_temporary_path() "temp.XXXXXX";
#if defined(_WIN32)
int fd = -1; wstring wfname = widen(fname);
wchar_t *buffer = wfname.data(); if (_wmktemp_s(buffer, wfname.length() 1)) return -1;
if (_wsopen_s(&fd, buffer, _O_CREAT | _O_RDWR | _O_WTEXT, _SH_DENYNO, _S_IREAD | _S_IWRITE)) {
return -1;
}
#else
char *buffer = fname.data();
int fd = mkstemp(buffer);
#endif
if (fd == -1) return -1;
file_write_string(fd, str);
#if defined(_WIN32)
_lseek(fd, 0, SEEK_SET);
#else
lseek(fd, 0, SEEK_SET);
#endif
return fd;
}
// open file descriptor with mode (read, write, append, etc).
int file_open(string fname, int mode) {
#if defined(_WIN32)
wstring wfname = widen(fname);
FILE *fp = nullptr;
switch (mode) {
case 0: { if (!_wfopen_s(&fp, wfname.c_str(), L"rb, ccs=UTF-8" )) break; return -1; }
case 1: { if (!_wfopen_s(&fp, wfname.c_str(), L"wb, ccs=UTF-8" )) break; return -1; }
case 2: { if (!_wfopen_s(&fp, wfname.c_str(), L"w b, ccs=UTF-8")) break; return -1; }
case 3: { if (!_wfopen_s(&fp, wfname.c_str(), L"ab, ccs=UTF-8" )) break; return -1; }
case 4: { if (!_wfopen_s(&fp, wfname.c_str(), L"a b, ccs=UTF-8")) break; return -1; }
default: return -1;
}
if (fp) { int fd = _dup(_fileno(fp));
fclose(fp); return fd; }
#else
FILE *fp = nullptr;
switch (mode) {
case 0: { fp = fopen(fname.c_str(), "rb" ); break; }
case 1: { fp = fopen(fname.c_str(), "wb" ); break; }
case 2: { fp = fopen(fname.c_str(), "w b"); break; }
case 3: { fp = fopen(fname.c_str(), "ab" ); break; }
case 4: { fp = fopen(fname.c_str(), "a b"); break; }
default: return -1;
}
if (fp) { int fd = dup(fileno(fp));
fclose(fp); return fd; }
#endif
return -1;
}
// close file descriptor.
int file_close(int fd) {
#if defined(_WIN32)
return _close(fd);
#else
return close(fd);
#endif
}
// check if regular file (non-directory) exists.
bool file_exists(string fname) {
std::error_code ec;
const findhardlinks::fs::path path = findhardlinks::fs::path(fname);
return (findhardlinks::fs::exists(path, ec) && ec.value() == 0 &&
(!findhardlinks::fs::is_directory(path, ec)) && ec.value() == 0);
}
// delete file or hardlink.
bool file_delete(string fname) {
std::error_code ec;
if (!file_exists(fname)) return false;
const findhardlinks::fs::path path = findhardlinks::fs::path(fname);
return (findhardlinks::fs::remove(path, ec) && ec.value() == 0);
}
// check if directory exists.
bool directory_exists(string dname) {
std::error_code ec;
const findhardlinks::fs::path path = findhardlinks::fs::path(dname);
return (findhardlinks::fs::exists(path, ec) && ec.value() == 0 &&
findhardlinks::fs::is_directory(path, ec) && ec.value() == 0);
}
// create directory recursively.
bool directory_create(string dname) {
std::error_code ec;
const findhardlinks::fs::path path = findhardlinks::fs::path(dname);
return (findhardlinks::fs::create_directories(path, ec) && ec.value() == 0);
}
// rename or move file to name and/or destination.
bool file_rename(string oldname, string newname) {
std::error_code ec;
if (!file_exists(oldname)) return false;
if (!directory_exists(filename_path(newname)))
directory_create(filename_path(newname));
const findhardlinks::fs::path path1 = findhardlinks::fs::path(oldname);
const findhardlinks::fs::path path2 = findhardlinks::fs::path(newname);
findhardlinks::fs::rename(path1, path2, ec);
return (ec.value() == 0);
}
// create hardlink.
bool hardlink_create(string fname, string newname) {
if (file_exists(fname)) {
if (!directory_exists(filename_path(newname)))
directory_create(filename_path(newname));
#if defined(_WIN32)
std::error_code ec;
const findhardlinks::fs::path path1 = findhardlinks::fs::path(fname);
const findhardlinks::fs::path path2 = findhardlinks::fs::path(newname);
findhardlinks::fs::create_hard_link(path1, path2, ec);
return (ec.value() == 0);
#else
return (!link(fname.c_str(), newname.c_str()));
#endif
}
return false;
}
} // anonymous namespace
// our test case:
int main(int argc, char **argv) {
int fd = file_open_from_string(filename_name(argv[0] ? argv[0] : "(null)"));
if (fd == -1) {
return 1; // failure
}
vector<string> dnames;
dnames.push_back(directory_get_temporary_path());
vector<string> p = findhardlinks::findhardlinks(fd, dnames, false);
/* close fist because Windows won't allow us to
move/rename it when opened; win32-only issue */
file_close(fd);
if (p.empty()) {
return 1; // failure
}
// rename original file to have hardlink number
vector<string> p2;
p2.push_back(p[0] " - hardlink 00");
file_rename(p[0], p2[0]);
// if file exists, create 99 hardlinks
if (file_exists(p2[0])) {
for (unsigned i = 1; i < 100; i ) {
if (!hardlink_create(p2[0], p[0] " - hardlink " ((std::to_string(i).length() == 1) ? ("0" std::to_string(i)) : std::to_string(i)))) {
return 1; // failure
}
}
}
// open original hardlink again, read-only
fd = file_open(p2[0], FD_RDONLY);
if (fd == -1) {
return 1;
}
// get hardlinks
p2 = findhardlinks::findhardlinks(fd, dnames, false);
file_close(fd);
// sort alphabetically, numerically
std::sort(p2.begin(), p2.end());
// print hardlink filenames, then delete them.
for (unsigned i = 0; i < p2.size(); i ) {
printf("%s\n", p2[i].c_str());
file_delete(p2[i]);
}
// done:
return 0;
}
上面的示例在創建硬鏈接后洗掉它們。的dnames引數findhardlinks()是一個字串向量,用于檢查多個檔案夾。如果是遞回的,請不要同時包含檔案夾及其子檔案夾以避免冗余。對于非命令列應用程式,有一個自動擴展環境變數的功能。例如:
#include <sstream>
#include "findhardlinks.hpp"
// ${PATH} environment variable delimiter.
#if defined(_WIN32)
#define PATH_DELIM ';'
#else
#define PATH_DELIM ':'
#endif
using std::string;
using std::vector;
#if defined(_WIN32)
using std::wstring;
using std::size_t;
#endif
/* necessary in GUI Windows applications to process window
clicks without crashing during lasting for/while loops. */
void message_pump() {
#if defined(_WIN32)
MSG msg; while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#endif
}
#if defined(_WIN32)
// UTF-8 support on Windows: string to wstring.
wstring widen(string str) {
size_t wchar_count = str.size() 1; vector<wchar_t> buf(wchar_count);
return wstring{ buf.data(), (size_t)MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buf.data(), (int)wchar_count) };
}
// UTF-8 support on Windows: wstring to string.
string narrow(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), nullptr, 0, nullptr, nullptr); vector<char> buf(nbytes);
return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, nullptr, nullptr) };
}
#endif
// split string by delimiter character.
vector<string> string_split(string str, char delimiter) {
vector<string> vec;
std::stringstream sstr(str);
string tmp;
while (std::getline(sstr, tmp, delimiter)) {
message_pump();
vec.push_back(tmp);
}
return vec;
}
// get environment variable value.
string environment_get_variable(string name) {
#if defined(_WIN32)
string value;
wchar_t buffer[32767];
wstring u8name = widen(name);
if (GetEnvironmentVariableW(u8name.c_str(), buffer, 32767) != 0) {
value = narrow(buffer);
}
return value;
#else
char *value = getenv(name.c_str());
return value ? value : "";
#endif
}
...
// our example:
vector<string> in = string_split(environment_get_variable("PATH"), PATH_DELIM);
// assuming "TMPDIR" and "HOME" exist in the calling process's environment:
in.push_back("${TMPDIR}"); // not %TMPDIR% or $TMPDIR.
in.push_back("${HOME}"); // use ${ENVVAR} to expand.
vector<string> out = findhardlinks::findhardlinks(fd, in, false);
// do something with "out" if the vector is not empty...
...
Let's say you have a file you want to delete and want to locate it, you know where one of its hard links are, but you'd like to locate and delete all of them. Here's an example:
#include <sstream>
#include "findhardlinks.hpp"
// ${PATH} environment variable delimiter.
#if defined(_WIN32)
#define PATH_DELIM ';'
#else
#define PATH_DELIM ':'
#endif
using std::string;
using std::vector;
/* read, write, append
open() permissions. */
enum {
FD_RDONLY,
FD_WRONLY,
FD_RDWR,
FD_APPEND,
FD_RDAP
};
// check if regular file (non-directory) exists.
bool file_exists(string fname) {
std::error_code ec;
const findhardlinks::fs::path path = findhardlinks::fs::path(fname);
return (findhardlinks::fs::exists(path, ec) && ec.value() == 0 &&
(!findhardlinks::fs::is_directory(path, ec)) && ec.value() == 0);
}
// open file descriptor with mode (read, write, append, etc).
int file_open(string fname, int mode) {
#if defined(_WIN32)
wstring wfname = widen(fname);
FILE *fp = nullptr;
switch (mode) {
case 0: { if (!_wfopen_s(&fp, wfname.c_str(), L"rb, ccs=UTF-8" )) break; return -1; }
case 1: { if (!_wfopen_s(&fp, wfname.c_str(), L"wb, ccs=UTF-8" )) break; return -1; }
case 2: { if (!_wfopen_s(&fp, wfname.c_str(), L"w b, ccs=UTF-8")) break; return -1; }
case 3: { if (!_wfopen_s(&fp, wfname.c_str(), L"ab, ccs=UTF-8" )) break; return -1; }
case 4: { if (!_wfopen_s(&fp, wfname.c_str(), L"a b, ccs=UTF-8")) break; return -1; }
default: return -1;
}
if (fp) { int fd = _dup(_fileno(fp));
fclose(fp); return fd; }
#else
FILE *fp = nullptr;
switch (mode) {
case 0: { fp = fopen(fname.c_str(), "rb" ); break; }
case 1: { fp = fopen(fname.c_str(), "wb" ); break; }
case 2: { fp = fopen(fname.c_str(), "w b"); break; }
case 3: { fp = fopen(fname.c_str(), "ab" ); break; }
case 4: { fp = fopen(fname.c_str(), "a b"); break; }
default: return -1;
}
if (fp) { int fd = dup(fileno(fp));
fclose(fp); return fd; }
#endif
return -1;
}
// close file descriptor.
int file_close(int fd) {
#if defined(_WIN32)
return _close(fd);
#else
return close(fd);
#endif
}
// delete file or hardlink.
bool file_delete(string fname) {
std::error_code ec;
if (!file_exists(fname)) return false;
const findhardlinks::fs::path path = findhardlinks::fs::path(fname);
return (findhardlinks::fs::remove(path, ec) && ec.value() == 0);
}
// split string by delimiter character.
vector<string> string_split(string str, char delimiter) {
vector<string> vec;
std::stringstream sstr(str);
string tmp;
while (std::getline(sstr, tmp, delimiter)) {
vec.push_back(tmp);
}
return vec;
}
// example:
int main(int argc, char **argv) {
int fd = file_open(((argc >= 2) ? argv[1] : ""), FD_RDONLY);
if (fd == -1) return 1; // error.
#if defined(_WIN32)
vector<string> in = string_split(((argc >= 3) ? argv[2] : "C:\\"), PATH_DELIM);
#else
vector<string> in = string_split(((argc >= 3) ? argv[2] : "/"), PATH_DELIM);
#endif
vector<string> out = findhardlinks::findhardlinks(fd, in, ((argc == 4) ? (bool)strtoul(argv[3], nullptr, 10) : false));
file_close(fd);
for (unsigned i = 0; i < out.size(); i ) {
file_delete(out[i]);
}
return 0;
}
Build:
g deletehardlinks.cpp -o deletehardlinks -std=c 17 -Wno-empty-body -static-libgcc -static-libstdc
Test (args: file,path,recursive):
./deletehardlinks test.txt . 0
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/437460.html
