如何使用NIX标准库删除文件夹中的所有文件,但不删除文件夹?

25

我正在尝试创建一个程序,用于删除Linux系统上 /tmp 文件夹中的内容。我的程序使用C/C++语言编写。

system("exec rm -r /tmp")

删除文件夹中的所有内容,但不删除文件夹本身。

是否有一种通过某种bash脚本调用system()的方式来实现此功能;或者是否有一种在C/C ++中可以直接完成的方法?

我的问题类似于这个问题,但我不在OS X上... 如何删除文件夹中的所有文件,但不删除文件夹本身?


2
rm -r /tmp/* 将会删除该文件夹中的所有内容。 - AusCBloke
1
如果你想要投资未来,你可以使用 boost::filesystem 来遍历并删除每一个文件。 - chris
3
嗯…… 这会删除文件夹中所有可见的文件(即通配符*扩展到的文件),但不会删除任何以点号 . 开头的文件。 - David Rodríguez - dribeas
1
@DavidRodríguez-dribeas:另外一边注意一下,删除 /tmp 的内容会有什么风险吗? - Finding Nemo 2 is happening.
是的,它可能会造成损害。许多程序假定该文件夹存在。因此,删除它并重新创建会导致竞争条件,并可能使其他程序崩溃。不要这样做。 - Lothar
显示剩余4条评论
8个回答

53
#include <stdio.h>
#include <dirent.h>

int main()
{
    // These are data types defined in the "dirent" header
    DIR *theFolder = opendir("path/of/folder");
    struct dirent *next_file;
    char filepath[256];

    while ( (next_file = readdir(theFolder)) != NULL )
    {
        // build the path for each file in the folder
        sprintf(filepath, "%s/%s", "path/of/folder", next_file->d_name);
        remove(filepath);
    }
    closedir(theFolder);
    return 0;
}

您不希望通过system()或类似方法生成新的shell - 这会产生很大的开销,却只是为了执行一些非常简单的操作,并且假设(和依赖)系统上可用的内容不必要。


1
嗯...一个适用于隐藏文件且不需要shell的解决方案! +1 (虽然另一方面它不会递归删除目录,也无法处理错误...) - David Rodríguez - dribeas
8
如果您不想删除文件“.”和“..”,则可能需要在while语句的开头设置if (0==strcmp(next_file->d_name, ".") || 0==strcmp(next_file->d_name, "..")) { continue; } - Daniel

16

在C/C++中,您可以这样做:

system("exec rm -r /tmp/*")
在Bash中,你可以这样做:
rm -r /tmp/*

这将删除/tmp内的所有内容,但不会删除/tmp目录本身。


7
嗯...这将删除文件夹中所有可见的文件(即* 扩展到的文件),但不会删除以点号开头的任何文件。 - David Rodríguez - dribeas
2
在 C 语言中,任何对 "system" 的调用都是不安全的。https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=2130132 - jackb
@jackb:我同意。然而(既然你投了反对票),请记住,OP确实问过:“是否有一种通过某种bash脚本调用系统的方式来完成这个任务的方法...” - Jay Sullivan
@jackb:我同意system不太好用,但我的回答只是在扩展原帖的问题,很明显是在使用system的情况下。您应该将您的观点发布为对原问题的反馈。 - Jay Sullivan
没有借口,其他用户提供了更正确的答案。我点赞了其中一个答案。我相信你下次会更加小心。 - jackb
system("exec rm ./Blurred/*"); 在Linux中对我有效。 - BarzanHayati

3
通过使用通配符*,您可以删除所有类型的文件。 system("exec rm -r /tmp/*")

嗯...这将删除文件夹中所有可见的文件(即*扩展到的文件),但不会删除以.开头的任何文件。 - David Rodríguez - dribeas

3
您可以做到的事情
system("exec find /tmp -mindepth 1 -exec rm {} ';'");

嗯...这将删除文件夹中所有可见的文件(即*扩展到的文件),但不会删除以.开头的任何文件。 - David Rodríguez - dribeas
@DavidRodríguez-dribeas 你是对的。我已经相应地编辑了我的答案。 - ALiX
任何对 System 的调用都不安全 https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=2130132 - jackb

2

在C/C++中,您可以使用(包括隐藏目录):

system("rm -r /tmp/* /tmp/.*");
system("find /tmp -mindepth 1 -delete");

但是如果在sh中没有“rm”或“find”实用程序,最好使用“ftw”和“remove”:

#define _XOPEN_SOURCE 500
#include <ftw.h>

static int remove_cb(const char *fpath, const struct stat *sb, int typeFlag, struct FTW *ftwbuf)
{
    if (ftwbuf->level)
        remove(fpath);
    return 0;
}

int main(void)
{
    nftw("./dir", remove_cb,  10, FTW_DEPTH);
    return 0;
}

2

我知道这是一个很老的问题,但在Demitri的优秀答案基础上,我创建了一个函数,如果需要,将递归删除子文件夹中的文件

它还进行了一些错误处理,以便传回errno。函数头是为doxygen解析而编写的。这个函数在我使用的简单示例情况下可以工作,并删除隐藏文件夹和隐藏文件。

我希望这能帮助未来的某个人

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#define SUCCESS_STAT 0

/**
 * checks if a specific directory exists
 * @param dir_path the path to check
 * @return if the path exists
 */
bool dirExists(std::string dir_path)
{
    struct stat sb;

    if (stat(dir_path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))
        return true;
    else
        return false;
}

/**
 * deletes all the files in a folder (but not the folder itself). optionally
 * this can traverse subfolders and delete all contents when recursive is true
 * @param dirpath the directory to delete the contents of (can be full or
 * relative path)
 * @param recursive true = delete all files/folders in all subfolders
 *                  false = delete only files in toplevel dir
 * @return SUCCESS_STAT on success
 *         errno on failure, values can be from unlink or rmdir
 * @note this does NOT delete the named directory, only its contents
 */
int DeleteFilesInDirectory(std::string dirpath, bool recursive)
{
    if (dirpath.empty())
        return SUCCESS_STAT;

    DIR *theFolder = opendir(dirpath.c_str());
    struct dirent *next_file;
    char filepath[1024];
    int ret_val;

    if (theFolder == NULL)
        return errno;

    while ( (next_file = readdir(theFolder)) != NULL )
    {
        // build the path for each file in the folder
        sprintf(filepath, "%s/%s", dirpath.c_str(), next_file->d_name);

        //we don't want to process the pointer to "this" or "parent" directory
        if ((strcmp(next_file->d_name,"..") == 0) ||
            (strcmp(next_file->d_name,"." ) == 0) )
        {
            continue;
        }

        //dirExists will check if the "filepath" is a directory
        if (dirExists(filepath))
        {
            if (!recursive)
                //if we aren't recursively deleting in subfolders, skip this dir
                 continue;

            ret_val = DeleteFilesInDirectory(filepath, recursive);

            if (ret_val != SUCCESS_STAT)
            {
                closedir(theFolder);
                return ret_val;
            }
        }

        ret_val = remove(filepath);
        //ENOENT occurs when i folder is empty, or is a dangling link, in
        //which case we will say it was a success because the file is gone
        if (ret_val != SUCCESS_STAT && ret_val != ENOENT)
        {
            closedir(theFolder);
            return ret_val;
        }

    }

    closedir(theFolder);

    return SUCCESS_STAT;
}

1

从C++17开始,您可以使用std::filesystem。下面的代码将使用directory_iterator列出目录中的所有文件和子目录,并调用remove_all来删除它们:

#include <filesystem>

namespace fs = std::filesystem;

void delete_dir_content(const fs::path& dir_path) {
    for (auto& path: fs::directory_iterator(dir_path)) {
        fs::remove_all(path);
    }
}

请注意,在底层操作系统API出现错误时,这将抛出一个filesystem_error异常。您可以通过以下方式避免这种情况:
void delete_dir_content(const fs::path& dir_path) {
    for (auto& path: fs::directory_iterator(dir_path)) {
        std::error_code err;
        std::uintmax_t n = fs::remove_all(path, err);
        if (static_cast<std::uintmax_t>(-1) == n) {
            std::cout << "Failed to remove_all(" << path << ") with error: " << err.message() << std::endl;
        }
    }
}

0
您可以使用nftw(3)。首先,进行一次遍历以收集要删除的文件路径集合。然后在第二次遍历中,对非目录文件使用unlink,对目录使用rmdir(2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接