Project2: 文件夹同步工具
任 务 背景 有 2 个文件夹 A 和 B ,经过 之前的同步操作 , 文 件夹 B 中的内容与文件夹 A 中内容完全一致 , 现 在用 户对 A 文件夹中部分文件 进 行了 编辑、 移 动、删 除或者新增的操作 ,请 你的程序根 据文件夹 A 中的内容 变 化 对 文件夹 B 进 行增量 更新 。
功能要求 文件夹 A 中保持不 变 的文件在文件夹 B 中保持不 变, 不更新 。 给 出文件夹 A 中文件的更新情况列表 , 即列出与上次 同步后文件夹 A 中所有 发 生 变 化的文件列表 , 包括 a) 与上次更新相比 , 所有新增文件的列表 b) 与上次更新相比 , 所有 删 除文件的列表 c) 与上次更新相比 , 所有 发 生 变动 的文件的列表 只 对 文件夹 A 中 发 生 变 化的文件在文件夹 B 中更新 , 即 针对 上述 3 中 变 化的文件 ,删 除文件夹 B 中的相 应 原 文件 , 并将 A 中最新文件复制到文件夹 B 中 。 注意 :删 除 时 需要提示用 户 是否确定 删 除源文件 。
其他要求 简单 易用 、 界面友好 足够的提示信息 输 入 错误 的 处 理 更新速度快
提高要求 将上述工具移植到 windows 平台下 。 在文件夹 B 中保留更新前的文件 ( 比如 , 重新 以特定的命名方式命名或者保存在特定的文 件夹中的方式 )。 开 发图 形界面
解 题 思路 简单 想法 : 遍 历 文件夹 A 、 B ,对 于 A 中的任一文件 f1, 在 B 中 查 找与 f1 同名文件 f2 如果找到 f2 ,读 取 f1 和 f2 的文件信息是否一致 一致 : 表示文件没有 发 生 变 化 , 什么也不做 不一致 : 用 f1 覆盖 f2 如果没有找到 , 表示 该 文件是新文件 ,则 将其 复制到文件夹 B 中 还 有没有其他 问题?
实现细节 如何遍 历 整个文件夹 ? Linux 命令 ls –R Find 调 用操作系 统 提供 API( 应 用 编 程接口 ) opendir() 、 readdir() 、 closedir() 如何 读 取文件信息 ? Linux 相关命令 : 系 统 函数 :
问题 1 : 遍 历 得到目 录 下所有的文件 方法 1 : 利用 linux 命令的 结 果 ls –R find
ls -R
find 命令 find 是 linux 中最有用的命令之一 用于在一个目 录( 及子目 录) 中搜索文件 可以指定一些匹配条件 , 如按文件名 、 文件类型 、 用 户 甚 至是 时间 戳 查 找文件 举 例 : find. -name "*.c” 在当前目 录 及其子目 录 中 查 找任何 扩 展名 为 c 的文件 find. -type f 查 找当前目 录 中的每一个普通文件 find / -mtime -5 在系 统 根目 录 下 查 找更改 时间 在 5 日以内的文件
如何在程序中利用 linux 命令的 结 果 system() 函数: 调用 “/bin/sh -c command” 执行特定的命令,阻 塞当前进程直到 command 命令执行完毕 如何得到命令 执 行的 结 果 system 的返回 值 不能待会命令的 执 行 结 果 输 入 输 出重定向 原型 : int system(const char *command); 返回 值: 如果无法启 动 shell 运行命令 , system 将返回 127 ; 出 现 不能 执 行 system 调 用的其他 错误时 返回 -1 。 如果 system 能够 顺 利 执 行 , 返 回那个命令的退出 码。
输 入 输 出重定向 Linux 重定向是指 对 原来系 统 命令的默 认执 行方式 进 行改 变 实现输 出 输 入的重定向 使用 时,相当于使用 0 简单举 例 cmd > file 把 cmd 命令的 输 出重定向到文件 file 中 。 如果 file 已 经 存在 ,则 清 空原有文件 , 例如 : ls -l >filenames.txt cmd < file 使 cmd 命令从 file 读 入
Linux 的管道 ls -l | wc -l
方法 1 :实现 提示 在你的程序中 , 利用 system 函数得到目 录 下 的所有文件 , 并将 结 果保存到文件中 system(“find. > filelist.txt”); system(“ls -l –R > filelist1.txt”);
方法 2 : 利用操作系 统 提供的 API 目 录 操作相关的 API opendir 打开目 录, 并返回 DIR* 形 态 的目 录 流 , 接下来 对 目 录 的 读 取和搜索都要使用此返回 值。 readdir 读 取目 录 返回一个代表目录里下一个项目的指针 closedir 关 闭 目 录
opendir : 打开目 录 表 头 文件 #include 定 义 函数 DIR * opendir(const char * name); 函数 说 明 opendir() 用来打开参数 name 指定的目 录, 并返 回 DIR* 形 态 的目 录 流 , 接下来 对 目 录 的 读 取和搜索 都要使用此返回 值。 返回 值 成功 则 返回 DIR* 型 态 的目 录 流 , 打开失 败则 返 回 NULL 。
readdir 函数 表头文件 #include 函数原型 struct dirent * readdir(DIR * dir); 函数说明 返回一个代表目录 dirp 里下一个项目的指向 dirent 结构的指 针。如果到达目录流结尾或出错它返回 NULL 。 返回值 成功则返回下个目录进入点。有错误发生或读取到目录文件 尾则返回 NULL 。
readdir 函数 (2) 在 Linux 系统里, dirent 结构定义如下: struct dirent { ino_t d_ino; /* inode number */ off_t d_off; /* 下一个项目的偏移 */ unsigned short d_reclen; /* 本记录的长度 */ unsigned char d_type; /* 文件类型;不是被所有文件系统支持 */ char d_name[256]; /* 文件名 */ };
closedir: 关 闭 目 录 表头文件 #include 定义函数 int closedir(DIR *dir); 函数说明 closedir() 关闭参数 dir 所指的目录流。 返回值 关闭成功则返回 0 ,失败返回 -1 ,错误原因存 于 errno 中 。
编 程提示 pDir=opendir(path) while( (ent=readdir(pDir))!=NULL ) { //do something; } closedir(pDir);
问题 2 : 如何确定文件是否更新 过? Linux 文件的 时间 信息 如何 读 取文件信息 ? Linux 相关命令 : stat 系 统 函数 : stat() 、 fstat() 、 lstat()
Linux 中文件 时间 访问时间 修改 时间 状 态 改 变时间 该 文件最后一次被修改的 时间 通 过 chmod 、 chown 命令修改一次文件属性 , 这 个 时间 就会更新 。 Windows 下 : 创 建 时间 修改 时间 访问时间
如何 获 取文件信息 Linux 相关命令: stat 系统函数: stat() 、 fstat() 、 lstat()
lstat 函数 返回与文件有关的信息 结 构 int lstat(const char *path, struct stat *buf); struct stat { dev_t st_dev; /* 文件所在 设备 的 标识 */ ino_t st_ino; /* 文件 结 点号 */ mode_t st_mode; /* 文件保 护 模式 */ nlink_t st_nlink; /* 硬 连 接数 */ uid_t st_uid; /* 文件用 户标识 */ gid_t st_gid; /* 文件用 户组标识 */ dev_t st_rdev; /* 文件所表示的特殊 设备 文件的 设备标识 */ off_t st_size; /* 总 大小 , 字 节为单 位 */ blksize_t st_blksize; /* 文件系 统 的 块 大小 */ blkcnt_t st_blocks; /* 分配 给 文件的 块 的数量 , 512 字 节为单 元 */ time_t st_atime; /* 最后 访问时间 */ time_t st_mtime; /* 最后修改 时间 */ time_t st_ctime; /* 最后状 态 改 变时间 */ };
代 码 分析 : 遍 历 目 录 #include void List( char *path, int indent ) { struct dirent* ent = NULL; DIR *pDir; char dir[512]; struct stat statbuf; if( (pDir=opendir(path))==NULL ) { fprintf( stderr, "Cannot open directory:%s\n", path ); return; }
while( (ent=readdir(pDir))!=NULL ) { // 得到 读 取文件的 绝对 路径名 snprintf( dir, 512,"%s/%s", path, ent->d_name ); // 得到文件信息 lstat( dir, &statbuf); // 判断是目 录还 是文件 if( S_ISDIR(statbuf.st_mode) ) { // 排除当前目 录 和上 级 目 录 if(strcmp( ".",ent->d_name) == 0 || strcmp( "..",ent->d_name) == 0) { continue; } // 如果是子目 录, 递归调 用函数本身, 实现 子目 录 中文件遍 历 printf( "%*s 子目 录 :%s/\n", indent, "", ent->d_name ); // 递归调 用, 遍 历 子目 录 中文件 List( dir, indent+4 ); } else { printf( "%*s 文件 :%s\n", indent, "", ent->d_name ); } closedir(pDir); }
int main(int argc, char* argv[]) { if(argc == 2) { List( argv[1], 2 ); } else { char *s = "."; List( s, 2 ); } return 0; }
其他 问题 提高效率 可以将文件信息保存起来 移植到 windows 平台 linux 下的 API 换 成 windows 平台的响 应 API