Linux下文件分发的算法设计及C代码实现
需求描述
在Linux系统的某个源目录中有一批后缀相同的文件,编写程序将这些文件按照前缀分发到不同的目录中。
例如,源目录SourceDir中存放有三个后缀相同的文件File1_1.txt、File2_1.txt和File3_1.txt,按照前缀File1_、File2_和File3_将它们分别移动(分发)到目录FileDir1、FileDir2和FileDir3中。
算法设计
基于需求,可以采用如图1所示的程序流程:
图1 程序总体流程
特殊流程考虑
在编写程序的过程中,对于某些特殊流程的考虑如下:
1.如果扫描源目录出错,则直接停止程序的运行,而不用继续扫描下一个目录。
2.对于某些空文件(即文件的大小为0),直接在源目录中将其删除,而不用进行分发。
3.为了随时能够处理放到源目录中的文件,程序每隔一段时间(如一分钟)扫描一次源目录。也就是说,如果不人为操作,程序启动之后会不停地运行。
程序代码
/********************************************************************** * 版权所有 (C)2016, Zhou Zhaoxiong。 * 文件名称:FileDistribute.c * 文件标识:无 * 内容摘要:将某个目录中的文件按照前缀分发到对应的目录中 * 其它说明:无 * 当前版本:V1.0 * 作 者:Zhou Zhaoxiong * 完成日期:20160517 **********************************************************************/ #include stdio.h #include string.h #include dirent.h #include ftw.h #include time.h // 重定义数据类型 typedef signed int INT32; typedef unsigned int UINT32; typedef unsigned char UINT8; // 全局变量定义 UINT8 g_szSourceDir[256] = {0}; // 源文件的目录 // 函数声明 INT32 SelectFlies(struct dirent *pDir); void ScanDirAndDistribute(void); void Sleep(UINT32 iCountMs);
* 修改日期 版本号 修改人 修改内容 * ------------------------------------------------------------- * 20160517 V1.0 Zhou Zhaoxiong 创建 ****************************************************************/ INT32 main(void) // 源文件的目录 snprintf(g_szSourceDir, sizeof(g_szSourceDir)-1, "%s/zhouzx/TestDir/SourceDir", getenv("HOME")); // 调用函数执行文件的分发 while (1) ScanDirAndDistribute(); Sleep(60 * 1000); // 每一分钟执行一次文件的分发 return 0;
/********************************************************************** * 功能描述:根据后缀选择文件 * 输入参数:dir-目录 * 输出参数:无 * 返 回 值:0-失败 1-成功 * 其它说明:一个形如test.txt的文件要被扫描出来, 而形如test的文件不符合条件 * 修改日期 版本号 修改人 修改内容 * -------------------------------------------------------------------- * 20160517 V1.0 ZhouZhaoxiong 创建 ***********************************************************************/ INT32 SelectFlies(struct dirent *pDir) if (pDir == NULL) printf("SelectFlies:input parameter is NULL!\n"); return 0; // 根据.txt(后缀)选择文件 if (strstr(pDir- d_name, ".txt") != NULL) return 1; // 找到了满足条件的文件 else return 0;
/********************************************************************** * 功能描述:扫描目录并分发文件 * 输入参数:无 * 输出参数:无 * 返 回 值:无 * 其它说明:无 * 修改日期 版本号 修改人 修改内容 * -------------------------------------------------------------------- * 20160517 V1.0 ZhouZhaoxiong 创建 ***********************************************************************/ void ScanDirAndDistribute(void) INT32 iScanDirRet = 0; UINT32 iFileIdx = 0; UINT32 iFileCount = 0; UINT32 iFileSize = 0; UINT8 szFileDir[256] = {0}; UINT8 szScanedFile[512] = {0}; UINT8 szCmdBuf[256] = {0}; FILE *fp = NULL; struct dirent **ppDirEnt = NULL; // 扫描源目录, 并分发文件 iScanDirRet = scandir(g_szSourceDir, ppDirEnt, SelectFlies, alphasort); if (iScanDirRet 0) // 扫描目录出错 printf("ScanDirAndDistribute:exec scandir failed, path=%s\n", g_szSourceDir); return; else if (iScanDirRet == 0) // 目录下无文件 printf("ScanDirAndDistribute:no satisfied file in directory %s\n", g_szSourceDir); else // 将满足条件的文件移动到对应的目录中 for (iFileIdx = 0; iFileIdx iScanDirRet; iFileIdx ++) // 先判断扫描到的文件是否为空文件, 是则直接删除, 不是才执行移动的操作 memset(szScanedFile, 0x00, sizeof(szScanedFile)); snprintf(szScanedFile, sizeof(szScanedFile) - 1, "%s/%s", g_szSourceDir, ppDirEnt[iFileIdx]- d_name); fp = fopen(szScanedFile, "r"); if (fp == NULL) // 打开文件失败, 直接返回 printf("ScanDirAndDistribute:open file %s failed, please check!\n", szScanedFile); return; fseek(fp, 0, SEEK_END); iFileSize = ftell(fp); if (iFileSize == 0) // 该文件为空文件 printf("ScanDirAndDistribute:%s is an empty file, so delete it directly!\n", szScanedFile); memset(szCmdBuf, 0x00, sizeof(szCmdBuf)); snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "rm %s", szScanedFile); system(szCmdBuf); else // 根据前缀将文件移动(分发)到对应的目录中 if (strncmp(ppDirEnt[iFileIdx]- d_name, "File1_", strlen("File1_")) == 0) // 移动到FileDir1 memset(szFileDir, 0x00, sizeof(szFileDir)); snprintf(szFileDir, sizeof(szFileDir)-1, "%s/zhouzx/TestDir/FileDir1", getenv("HOME")); else if (strncmp(ppDirEnt[iFileIdx]- d_name, "File2_", strlen("File2_")) == 0) // 移动到FileDir2 memset(szFileDir, 0x00, sizeof(szFileDir)); snprintf(szFileDir, sizeof(szFileDir)-1, "%s/zhouzx/TestDir/FileDir2", getenv("HOME")); else if (strncmp(ppDirEnt[iFileIdx]- d_name, "File3_", strlen("File3_")) == 0) // 移动到FileDir3 memset(szFileDir, 0x00, sizeof(szFileDir)); snprintf(szFileDir, sizeof(szFileDir)-1, "%s/zhouzx/TestDir/FileDir3", getenv("HOME")); else // 前缀不满足, 直接将该文件删掉 memset(szCmdBuf, 0x00, sizeof(szCmdBuf)); snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "rm %s", szScanedFile); system(szCmdBuf); printf("ScanDirAndDistribute:now, %s\n", szCmdBuf); continue; // 继续判断下一个 memset(szCmdBuf, 0x00, sizeof(szCmdBuf)); snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "mv %s %s", szScanedFile, szFileDir); system(szCmdBuf); printf("ScanDirAndDistribute:now, %s\n", szCmdBuf); iFileCount ++; printf("ScanDirAndDistribute:this time,totally moved %d file(s).\n", iFileCount); return;
/********************************************************************** * 功能描述: 程序休眠 * 输入参数: iCountMs-休眠时间(单位:ms) * 输出参数: 无 * 返 回 值: 无 * 其它说明: 无 * 修改日期 版本号 修改人 修改内容 * ------------------------------------------------------------------ * 20160517 V1.0 Zhou Zhaoxiong 创建 ********************************************************************/ void Sleep(UINT32 iCountMs) struct timeval t_timeout = {0}; if (iCountMs 1000) t_timeout.tv_sec = 0; t_timeout.tv_usec = iCountMs * 1000; else t_timeout.tv_sec = iCountMs / 1000; t_timeout.tv_usec = (iCountMs % 1000) * 1000; select(0, NULL, NULL, NULL, t_timeout); // 调用select函数阻塞程序 }
程序测试
将编写好的程序“FileDistribute.c”上传到Linux机器,并使用“gcc -g -o FileDistribute FileDistribute.c”命令对该程序进行编译,生成“FileDistribute”文件。下面对程序进行详细的测试。
1.在启动程序之前,在源目录SourceDir中放入文件File1_1.txt、File2_1.txt和File3_1.txt,程序运行情况如下:
ScanDirAndDistribute:now, mv /home/zhou/zhouzx/TestDir/SourceDir/File1_1.txt /home/zhou/zhouzx/TestDir/FileDir1
ScanDirAndDistribute:now, mv /home/zhou/zhouzx/TestDir/SourceDir/File2_1.txt /home/zhou/zhouzx/TestDir/FileDir2
ScanDirAndDistribute:now, mv /home/zhou/zhouzx/TestDir/SourceDir/File3_1.txt /home/zhou/zhouzx/TestDir/FileDir3
ScanDirAndDistribute:this time,totally moved 3 file(s).
可以看到,源目录中的三个文件已经没有了,它们被分别移动到了结果目录FileDir1、FileDir2和FileDir3中:
~/zhouzx/TestDir/SourceDir ll
total 0
~/zhouzx/TestDir/FileDir1 ll
-rw------- 1 zhou users 12 2016-05-17 18:58 File1_1.txt
~/zhouzx/TestDir/FileDir2 ll
-rw------- 1 zhou users 12 2016-05-17 18:58 File2_1.txt
~/zhouzx/TestDir/FileDir3 ll
-rw------- 1 zhou users 12 2016-05-17 18:58 File3_1.txt
2.一段时间之后,在源目录SourceDir中放入文件File4_1.txt,程序运行情况如下:
ScanDirAndDistribute:now, rm /home/zhou/zhouzx/TestDir/SourceDir/File4_1.txt
ScanDirAndDistribute:this time,totally moved 0 file(s).
可以看到,因为前缀不匹配,File4_1.txt文件直接被删除掉了。
~/zhouzx/TestDir/SourceDir ll
total 0
3.一段时间之后,在源目录SourceDir中放入空文件File_7.txt、File_8.txt和File_9.txt,程序运行情况如下:
ScanDirAndDistribute:/home/zhou/zhouzx/TestDir/SourceDir/File_7.txt is an empty file, so delete it directly!
ScanDirAndDistribute:/home/zhou/zhouzx/TestDir/SourceDir/File_8.txt is an empty file, so delete it directly!
ScanDirAndDistribute:/home/zhou/zhouzx/TestDir/SourceDir/File_9.txt is an empty file, so delete it directly!
ScanDirAndDistribute:this time,totally moved 0 file(s).
可以看到,源目录SourceDir中的空文件已经被全部删除掉了。
~/zhouzx/TestDir/SourceDir ll
total 0
需求扩展
基于本文中的需求和程序,可考虑对需求进行以下扩展:
1.在移动(分发)文件之前,先查看相同文件名的文件在对应结果目录中是否存在,如果存在,则直接将该文件在源目录中删除掉;如果不存在,才将该文件移动到对应结果目录中。
2.为避免结果目录中的文件过多,可以在程序中添加清理机制,即将存放时间超过一定时长的文件删除掉。
3.为了体现程序的灵活性,可将部分文件信息(如文件前缀、后缀、存放目录、扫描间隔时长等)存放到配置文件中,程序在启动时读取相关的配置项的值来执行后续目录扫描和文件分发的操作。
Android C++系列:Linux文件IO操作(二) 注意这个读写位置和使用C标准I/O库时的读写位置有可能不同,这个读写 位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区中的位置。比如用fgetc读一个字节,fgetc有可能从内核中预读1024个字节到I/O缓冲区中,再返回第一 个字节,这时该文件在内核中记录的读写位置是1024,而在FILE结构体中记录的读写位置是 1。
Android C++系列:Linux文件IO操作(一) 事实上Unbuffered I/O这个名词是有些误导的,虽然write系统调用位于C标准库I/O缓 冲区的底层,但在write的底层也可以分配一个内核I/O缓冲区,所以write也不一定是直接 写到文件的,也可能写到内核I/O缓冲区中,至于究竟写到了文件中还是内核缓冲区中对于 进程来说是没有差别的,如果进程A和进程B打开同一文件,进程A写到内核I/O缓冲区中的数 据从进程B也能读到,而C标准库的I/O缓冲区则不具有这一特性(想一想为什么)
周兆熊 硕士研究生毕业于南京邮电大学计算机应用技术专业,曾在中兴通讯从事软件开发工作,现就职于重庆某物联网技术公司,任高级嵌入式软件开发工程师。著有《C程序员从校园到职场》一书,同时也是《信息通信技术百科全书》的编者之一及CSDN博客专家。QQ(微信):245924426。
相关文章
- RedisDesktopManager连接远程Linux系统的Redis服务(图文)
- Java环境配置(linux安装jdk8)
- linux 将一个服务器上的文件或者文件夹复制到另一台服务器上
- Linux 进程
- Linux下掌控磁盘分区的九大神器
- 手把手教你安装Xubuntu Linux
- 如何在 Linux 中压缩及解压缩 .bz2 文件
- linux 文件文件夹操作
- Linux命令之tar
- Linux查看内核信息或系统信息
- linux通常使用的 rc 和 .(点)文件
- Linux下统计当前文件夹下的文件个数、目录个数
- 查看Linux系统版本信息
- linux编程fcntl获取和设置文件锁
- 浅析linux安装pm2后报错pm2: command not found的问题
- Linux 系统的IP与域名解析文件[局域网的DNS]
- 【正点原子MP157连载】 第十章 U-boot使用-摘自【正点原子】【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
- Linux中文件特殊权限suid、sgid、sticky(有图详细讲解)
- Linux环境PHP7.0安装
- 以 Ubuntu 为例:清理 linux 系统的"垃圾"文件