查找并重命名文件

Posted by farmer3-c on January 13, 2026

下面是实现查找并重命名文件的C程序,使用了apue.h库中的函数和数据结构。 该程序支持查找指定目录下的所有文件,并将指定文件名的文件重命名为”pass”。 程序会输出重命名的文件数量。

点击展开/折叠代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include "apue.h"
#include <dirent.h>
#include <limits.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

typedef int Myfunc(const char *, const struct stat *, int);

static Myfunc myfunc_rename;
static int dopath(Myfunc *);
static int myftw(char *, Myfunc *);
static char *fullpath;          // 存储文件完整路径
static char *target_filename;   // 要查找并重命名的目标文件名
static int rename_count = 0;    // 成功重命名的文件数

// 文件类型标记
#define FTW_F    1    /* 普通文件 */
#define FTW_D    2    /* 目录 */
#define FTW_DNR  3    /* 不可读目录 */
#define FTW_NS   4    /* 无法stat的文件 */

int main(int argc, char *argv[])
{
    int ret;

    // 校验参数:支持 myfind <指定目录> <要改名的文件名>
    if (argc != 3) {
        err_quit("usage: myfind <target_directory> <filename_to_rename>");
    }

    target_filename = argv[2];
    rename_count = 0;

    ret = myftw(argv[1], myfunc_rename);

    if (rename_count == 0) {
        printf("未找到名为 '%s' 的文件,无需重命名\n", target_filename);
    } else {
        printf("成功将 %d 个文件重命名为 'pass'\n", rename_count);
    }

    exit(ret);
}

static int myftw(char *pathname, Myfunc *func)
{
    size_t len;
    fullpath = path_alloc(&len);  // 从apue库获取路径缓冲区
    strncpy(fullpath, pathname, len);
    fullpath[len-1] = 0;          // 确保字符串终止
    return(dopath(func));
}

static int dopath(Myfunc *func)
{
    struct stat statbuf;
    struct dirent *dirp;
    DIR *dp;
    int ret;
    char *ptr;

    // 获取文件属性失败
    if (lstat(fullpath, &statbuf) < 0)
        return(func(fullpath, &statbuf, FTW_NS));

    // 如果是普通文件,执行重命名逻辑
    if (S_ISDIR(statbuf.st_mode) == 0)
        return(func(fullpath, &statbuf, FTW_F));

    // 如果是目录,先执行回调(无实际操作)
    if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
        return(ret);

    // 拼接子目录路径
    ptr = fullpath + strlen(fullpath);
    *ptr++ = '/';
    *ptr = 0;

    // 打开目录失败
    if ((dp = opendir(fullpath)) == NULL)
        return(func(fullpath, &statbuf, FTW_DNR));

    // 遍历目录下的所有文件/子目录
    while ((dirp = readdir(dp)) != NULL) {
        // 跳过.和..
        if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
            continue;

        // 拼接子文件路径
        strcpy(ptr, dirp->d_name);
        // 递归处理子文件/子目录
        if ((ret = dopath(func)) != 0)
            break;
    }

    // 恢复路径(移除最后拼接的子文件名)
    ptr[-1] = 0;

    // 关闭目录
    if (closedir(dp) < 0)
        err_ret("无法关闭目录: %s", fullpath);

    return(ret);
}

// 查找目标文件并将其重命名为pass
static int myfunc_rename(const char *pathname, const struct stat *statptr, int type)
{
    // 仅处理普通文件
    if (type != FTW_F)
        return 0;

    // 提取文件名(路径最后一个/后的部分)
    char *filename = strrchr(pathname, '/');
    if (filename == NULL) {
        filename = (char *)pathname;  // 无/,直接是文件名
    } else {
        filename++;  // 跳过/,指向实际文件名
    }

    // 匹配到目标文件,执行重命名
    if (strcmp(filename, target_filename) == 0) {
        // 拼接新路径:原目录 + /pass
        char new_path[PATH_MAX];
        strncpy(new_path, pathname, strrchr(pathname, '/') - pathname + 1);
        strcat(new_path, "pass");

        // 执行重命名
        if (rename(pathname, new_path) == 0) {
            printf("成功重命名: %s -> %s\n", pathname, new_path);
            rename_count++;
        } else {
            err_ret("重命名失败: %s", pathname);
        }
    }

    return 0;
}

UNIX系统中路径的操作与修改

这个程序实现了在指定目录下查找并重命名特定文件的功能,展示了多种路径操作技术。

一、UNIX路径的基本表示

在UNIX系统中,路径是用于标识文件或目录位置的字符串:

  • 使用正斜杠 / 作为路径分隔符
  • 绝对路径从根目录 / 开始
  • 相对路径从当前工作目录开始
  • . 表示当前目录,.. 表示父目录

二、程序中的路径操作技术

1. 路径缓冲区管理

1
static char *fullpath;  // 存储文件完整路径

程序使用 fullpath 变量存储文件的完整路径,通过 path_alloc 函数(来自apue库)安全地分配足够大的缓冲区:

1
2
3
fullpath = path_alloc(&len);  // 从apue库获取路径缓冲区
strncpy(fullpath, pathname, len);
fullpath[len-1] = 0;          // 确保字符串终止

这确保了路径操作时有足够的空间,避免缓冲区溢出。

2. 目录遍历与路径构建

程序使用深度优先遍历(DFS)方式遍历目录树:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 拼接子目录路径
ptr = fullpath + strlen(fullpath);
*ptr++ = '/';
*ptr = 0;

// 遍历目录下的所有文件/子目录
while ((dirp = readdir(dp)) != NULL) {
    // 跳过.和..
    if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
        continue;

    // 拼接子文件路径
    strcpy(ptr, dirp->d_name);
    // 递归处理子文件/子目录
    if ((ret = dopath(func)) != 0)
        break;
}

// 恢复路径(移除最后拼接的子文件名)
ptr[-1] = 0;

这段代码展示了如何动态构建和恢复路径:

  • 先获取当前路径长度,在末尾添加 /
  • 将子文件名拼接到路径末尾
  • 递归处理后,通过将最后一个 / 设为 \0 恢复路径

3. 文件名提取

程序使用 strrchr 函数提取路径中的文件名:

1
2
3
4
5
6
7
// 提取文件名(路径最后一个/后的部分)
char *filename = strrchr(pathname, '/');
if (filename == NULL) {
    filename = (char *)pathname;  // 无/,直接是文件名
} else {
    filename++;  // 跳过/,指向实际文件名
}

strrchr 函数查找字符串中最后一次出现指定字符(这里是 /)的位置,通过这种方式可以从完整路径中分离出文件名。

4. 文件重命名操作

程序使用 rename 函数进行文件重命名,这涉及到路径的修改:

1
2
3
4
5
6
7
8
9
10
11
12
// 拼接新路径:原目录 + /pass
char new_path[PATH_MAX];
strncpy(new_path, pathname, strrchr(pathname, '/') - pathname + 1);
strcat(new_path, "pass");

// 执行重命名
if (rename(pathname, new_path) == 0) {
    printf("成功重命名: %s -> %s\n", pathname, new_path);
    rename_count++;
} else {
    err_ret("重命名失败: %s", pathname);
}

这段代码实现了如何构建新路径:

  • 首先复制原路径中目录部分(从开头到最后一个 /
  • 然后在目录路径后拼接新的文件名 “pass”
  • 使用 rename 函数将原文件路径修改为新路径

总结

UNIX系统中路径的操作和修改是通过字符串处理函数、目录操作函数和文件系统调用的组合实现的。程序展示了如何构建路径、遍历目录树并修改文件路径。

通过学习这些技术,我们可以更好地理解UNIX系统中文件系统的组织方式,以及如何在程序中有效地操作和修改路径。