news 2026/9/2 2:46:47

用C脚本清理C盘:从零实现高效安全的系统清理工具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用C脚本清理C盘:从零实现高效安全的系统清理工具

简介:这是一份基于Bison与Flex构建的C脚本语言实现包,适合学习编译原理、词法/语法分析以及交互式解释器(REPL)设计的中高级开发者,也可作为系统级编程课程或编译原理实验的参考项目。项目源自高中系统级编程课程,代码组织完整,包含词法与语法规则定义、哈希表与工具函数实现、构建脚本和测试用例,能够直接运行并快速理解语言前端构造流程。压缩包共12个文件,主要类型为C源文件、头文件、Yacc/Lex规则文件、Makefile与说明文档,整体仅15KB,体积小巧,便于阅读、修改和二次开发。语言内置REPL,支持命令行历史导航,并实现了字符串索引、子串提取、逆序和查找等常用操作,可作为理解脚本引擎实现细节的微型示例。目前已有361人学习,对于想通过具体项目掌握Bison/Flex用法的读者,具有直接参考价值。 写C脚本(c-script)这个主题,得先从一个很现实的场景说起:C盘满了,网上到处都有人分享"C盘清理脚本bat下载",下载下来一看,无非就是几行del、rd命令,删的是Windows临时文件夹、浏览器缓存、系统更新残留。能用,但也就那回事。删的过程中经常遇到文件被占用、权限不足直接中断,有的bat代码里还混着一堆第三方下载链接,运行完还弹广告。我后来干脆放弃了纯bat方案,改用C语言写了一个"脚本化"的小工具——单文件、编译成exe、双击就跑,我把这类用C语言快速实现、像脚本一样即写即用的工具统称为C脚本(c-script)。这篇文章就把这套思路完整拆开,从概念、对比到代码实现,给出一套能直接落地的C盘清理方案。

1. C脚本到底是什么

1.1 我理解的c-script:用C语言写"即用即走"的工具

严格意义上,C是一门编译型语言,跟"脚本"这个词看起来不太搭。脚本通常指边解释边执行的代码,比如bat、Shell、Python。但在实际工作中,我理解的c-script是一种用法上的定位:用C语言编写一个功能单一、逻辑直接、面向具体任务的单文件程序,它的开发节奏完全是脚本式的——写、编译、运行、看结果,不行就改,30分钟出一个能用的小工具。它不需要搭建工程、不需要分层架构、不需要接口设计,本质上是把C语言当成"加强版脚本"在用。

这个定位和许多开源项目里叫"c-script"的东西也算对得上:把C代码嵌在文本文件里,通过一个解释器或加载器直接执行。比如有人用TCC(Tiny C Compiler)把C源码当脚本跑,有人写一个运行时动态加载.so/.dll,还有人做嵌入式系统里的C脚本引擎。它们的共同点是:保留C的表达能力,去掉"工程化"的包袱

我在做C盘清理工具时用的就是这套思路。功能需求很明确:遍历指定目录、计算文件大小、删除过期临时文件、输出日志。这种逻辑用bat写会非常痛苦,变量处理、延迟展开、编码转换、错误码判断,每一处都在劝退。用Python写倒是舒服,但机器上不一定装了解释器,打包成exe体积动辄几十MB,杀毒软件还经常误报。C语言反而是那个"刚刚好"的选项——体积小、零依赖、直接调用Windows API处理文件路径和权限,天生的系统级操作能力。

1.2 和bat、Python相比,C脚本强在哪、弱在哪

拿C盘清理这个具体场景来对比,很多问题会看得更清楚。bat脚本的痛点在于:一是语法太简陋,几乎没有数据结构,复杂逻辑全靠标签跳转,写200行基本就是灾难现场;二是错误处理能力接近于零,del \s遇到权限不足只会打一行红字,没有try-catch,没有异常分支;三是编码问题顽固,现代Windows默认UTF-8,老系统又是GBK,中文路径在bat里来回乱码。

Python的问题在于分发:脚本模式需要目标机器装Python,exe模式打包体积大,而且PyInstaller打出来的程序常年被各种杀毒引擎标记为病毒,作为清理工具反而"名声在外"。

C脚本的优势比较明显:

  • 编译产物是原生exe,体积可以做到几十KB到几百KB,一般不会触发杀软误报。
  • 直接调用系统API,对Windows的文件权限、文件属性、回收站机制有最底层的控制力。
  • 代码结构和错误处理是完整的编程语言级体验,写复杂逻辑不痛苦。

劣势也有:C的字符串处理、内存管理确实比现代脚本语言繁琐,写起来"手感"更重。但如果任务本身就是系统级的文件操作,C的标准库加Windows API基本是降维打击。

维度batPython脚本C脚本
运行依赖系统自带需解释器或打包无依赖,原生exe
体积几KB打包后20-100MB30KB-1MB
字符串与编码灾难优秀一般但可控
Windows API调用受限需第三方库直接调用
错误处理
开发速度快但不可维护较快中等

这张表不是想说C有多万能,而是说明在"系统清理"这类需要高强度调用系统能力、又对分发体积有要求的场景里,C脚本是性价比很高的选择。

2. 写C盘清理工具之前,先把清理范围和边界想清楚

2.1 哪些目录值得清理,哪些不要碰

C盘"满"的原因很多,但大部分可安全清理的空间集中在几个固定位置。以Windows 10/11为例,我长期保持的经验值如下:

目录说明可清理性
C:\Windows\Temp系统临时文件,开机后很多已被释放高,但部分文件被占用
%TEMP%(用户Temp)用户级临时文件,软件运行缓存
C:\Windows\SoftwareDistribution\DownloadWindows更新下载缓存高,但需先停止更新服务
C:\Windows\Prefetch预读取文件,加快启动速度用中,删了会重建
回收站用户删除的文件
C:\ProgramData\Microsoft\Windows\WERWindows错误报告
浏览器缓存(Chrome/Edge)各类Web缓存中,需识别具体路径

我选择的目标是前5种,并且有一个核心原则:只清空目录内的文件,不删除目录本身;只删除满足条件的文件,不按"全删"逻辑来处理。比如Prefetch里有个别正在被系统使用的文件,如果整体删除会报错中断,影响后面其他目录的清理流程。

2.2 安全底线:白名单优先,绝不盲删

C盘清理工具翻车案例网上太多了,基本都是"删了不该删的"。所以我在设计时的安全底线有三个:

第一,白名单优先。所谓白名单,是只清理我自己逻辑里明确指定的目录,而不是让用户输入一个路径就扫。这样可以防止手滑把C盘根目录整个扫一遍。

第二,按文件后缀和修改时间过滤。临时文件基本都有明确的特征,比如后缀是.tmp.log.old,或者文件名包含~号。我会把删除条件限定为"指定目录+指定后缀+修改时间超过7天",基本不会误伤。

第三,设计dry-run模式。也就是"演练模式",程序只统计、只报告、不实际删除。实际体验过的人都会发现,多这个模式等于多了一条命。

2.3 Windows API选型:搞定文件遍历和删除

在Windows上遍历目录,最常用的不是C标准库的opendir,而是Windows API的FindFirstFileWFindNextFileW。原因很简单:标准库处理不了长路径和非UTF-8编码的系统文件名。用宽字符版本的API,配合GetFinalPathNameByHandleW,基本能覆盖绝大多数场景。

删除文件时用DeleteFileW,删除目录时用RemoveDirectoryW。如果要"清空目录但保留目录结构",就写成递归逻辑:先遍历删除文件,再递归进入子目录,最后子目录删空了就删掉整个空目录。

这里有一个很容易踩的坑:很多临时文件被设置成了只读属性DeleteFileW遇到只读文件会直接失败,所以删除前需要调用SetFileAttributesWFILE_ATTRIBUTE_READONLY清掉。这个细节在bat脚本里通常没法优雅处理,在C里就一行代码的事。

3. 实操:从零写一个可复用的C脚本清理工具

3.1 环境准备:MinGW-w64就够了

写C脚本不需要Visual Studio那种重量级IDE,装一个MinGW-w64编译器就够。我长期用的组合是:

  • 编译器:MinGW-w64(X86_64编译器)或TCC(Tiny C Compiler,更轻)
  • 编辑器:任意文本编辑器
  • 构建方式:命令行gcc/gcc命令

MinGW-w64的安装方式这里不展开,装好后在命令行验证一下gcc --version能输出版本号即可。

用TCC也是一种很有意思的实践,它的编译速度极快,几百行的C代码编译时间小于0.1秒,非常接近"解释执行"的体验。如果你追求极致的"脚本感",TCC值得一试。后面说的编译命令以gcc为例,换成tcc基本也是同一套用法。

3.2 核心代码逐段拆解

下面这套代码是我实际在用的一个简化版,删掉了一些与具体机器相关的特殊目录,保留主干逻辑,方便直接参考复现。

#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdio.h> #include <string.h> #include <time.h> int dry_run = 1; // 1=演练模式,0=实际删除 long long total_freed = 0; // 释放空间统计 // 检查文件是否超过 N 天未修改 int is_old_enough(const char* path, int days) { WIN32_FILE_ATTRIBUTE_DATA attr; if (!GetFileAttributesExA(path, GetFileExInfoStandard, &attr)) return 0; SYSTEMTIME st; FILETIME lt; FileTimeToLocalFileTime(&attr.ftLastWriteTime, &lt); FileTimeToSystemTime(&lt, &st); time_t now = time(NULL); struct tm tm_last = {0}; tm_last.tm_year = st.wYear - 1900; tm_last.tm_mon = st.wMonth - 1; tm_last.tm_mday = st.wDay; time_t last_t = mktime(&tm_last); return (difftime(now, last_t) > days * 86400) ? 1 : 0; }

这段函数的核心是时间判断。系统临时文件有个特点——很多文件在程序退出后就永远没人访问了,但它们不会自动消失。用"最后写入时间超过7天"作为过滤条件,能有效避开正在被运行的软件频繁写入的文件,同时把真正没用的旧文件筛出来。

接下来是删除逻辑和目录遍历:

void clean_directory(const char* base_path, int days) { char pattern[MAX_PATH]; char full_path[MAX_PATH]; WIN32_FIND_DATAA ffd; HANDLE hFind; snprintf(pattern, MAX_PATH, "%s\\*", base_path); hFind = FindFirstFileA(pattern, &ffd); if (hFind == INVALID_HANDLE_VALUE) return; do { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; snprintf(full_path, MAX_PATH, "%s\\%s", base_path, ffd.cFileName); if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // 递归清理子目录 clean_directory(full_path, days); // 子目录清空后可尝试删除该目录本身 if (!dry_run) { SetFileAttributesA(full_path, FILE_ATTRIBUTE_NORMAL); RemoveDirectoryA(full_path); } } else { // 文件:先判断时间,再删除 if (!is_old_enough(full_path, days)) continue; LARGE_INTEGER fsize; fsize.LowPart = ffd.nFileSizeLow; fsize.HighPart = ffd.nFileSizeHigh; if (dry_run) { printf("[模拟] %s (%.2f MB)\n", full_path, fsize.QuadPart / 1024.0 / 1024.0); } else { SetFileAttributesA(full_path, FILE_ATTRIBUTE_NORMAL); if (DeleteFileA(full_path)) { printf("[删除] %s\n", full_path); total_freed += fsize.QuadPart; } } } } while (FindNextFileA(hFind, &ffd) != 0); FindClose(hFind); }

这段代码值得注意的有三个设计点:

一是先清空子目录再删空目录的顺序。如果不先递归清理子目录,直接尝试删除目录会因为目录非空而失败。这也是很多bat脚本清理不彻底的原因——它们用rd /s删除目录,一旦中间某一步被占用就整个中断,而不是"跳过占用文件,继续删其他文件"。

二是删除前先清掉只读属性。Windows临时目录里只读文件极其常见,bat里遇到只读文件会报"拒绝访问",C里一行SetFileAttributesA就解决了。

三是dry_run模式。运行时不加任何参数是所有命令的危险时刻,如果默认实际删除,很容易翻车。把默认值设为1(模拟模式),用户得明确加--exec参数才会真正执行删除,这个习惯可以推广到所有这类工具上。

3.3 主函数与参数设计:让工具像脚本一样灵活

接着是主函数。一个"脚本感"明确的小工具,参数设计应该遵循"默认安全、显式执行"的原则:

int main(int argc, char* argv[]) { printf("C脚本 - C盘清理工具\n"); if (argc > 1 && strcmp(argv[1], "--exec") == 0) { dry_run = 0; } const char* targets[] = { "C:\\Windows\\Temp", "C:\\Windows\\SoftwareDistribution\\Download", "C:\\Windows\\Prefetch" }; int days = 7; for (int i = 0; i < 3; i++) { printf("\n== 清理 %s ==\n", targets[i]); clean_directory(targets[i], days); } if (dry_run) { printf("\n[演练模式] 以上为待清理文件。确认无误后,使用 --exec 参数实际执行。\n"); } else { printf("\n清理完成,共释放 %.2f MB\n", total_freed / 1024.0 / 1024.0); } return 0; }

运行方式非常简单:

# 先演练,看看会删什么 clean_c_drive.exe # 确认没问题后实际执行 clean_c_drive.exe --exec

如果觉得每次都手动切到目录里运行太麻烦,可以把编译好的exe放到C:\tools\下,再去PATH里加上这个路径,以后在任意一个命令行窗口里敲clean_c_drive --exec就能清理,这个用法已经非常接近"C脚本"的定位了。

3.4 编译与分发:单exe怎么瘦身、怎么签名

编译命令很简单,我通常用下面这一行:

gcc -O2 -s -o clean_c_drive.exe clean_c_drive.c

-O2是优化,-s是strip掉调试符号,能把体积从几十KB削到几KB。编译出来的exe复制到任何Windows 10/11机器上都能直接运行,不需要安装任何运行时。

如果还想更进一步,可以把几个小工具合并成一个exe,通过子命令区分,比如cleaner.exe tempcleaner.exe cachecleaner.exe all。这种用法的本质是一个"命令分发器",跟脚本工具集的思路很像,也是我后续扩展的方向。

要注意的是,如果分发给别人用,建议加个自定义数字证书或者至少加版本资源。未经签名的exe在SmartScreen上会弹"Windows已保护你的电脑",虽然几行代码的事却能极大降低使用者的心理防线。加版本资源需要写一个.rc资源文件,再配合windres工具,这里不展开,想了解可以查MinGW的资源编译文档。

3.5 和bat配合:让C脚本驱动旧批处理流程

有人会问:既然都有C脚本了,为什么还要提bat?实际使用中,bat依然有它的生态价值:双击执行、右键菜单、任务计划程序都原生支持bat。最优实践是"bat做壳,exe干活"。

写一个启动器bat:

@echo off chcp 65001 >nul cd /d C:\tools clean_c_drive.exe --exec pause

bat只负责三件事:切代码页、切目录、调用exe。真正的清理逻辑全在C里面。这样既享受了bat的"入口便利性",又规避了bat写复杂逻辑的所有缺点。

更进一步的玩法是配合Windows任务计划程序,让这个工具每周一凌晨自动运行一次。任务计划里执行的对象直接指向clean_c_drive.exe --exec即可,不需要中间套bat。这样C盘清理就变成了无人值守的定时任务。

4. 常见问题与排查技巧实录

4.1 文件被占用导致删除失败,怎么不中断整个流程

现象:清理C:\Windows\Temp时,总有那么几个文件正在被系统或软件占用,DeleteFileA返回失败,但后续文件仍然可以正常删除。我在代码里对删除失败没有做特殊处理,只是跳过了,日志里继续打印下一个文件。

这里有个设计原则:清理工具的核心是"能删多少删多少",而不是"必须全删干净"。bat脚本常见的毛病就是遇到一个失败就终止整个循环。我建议在关键地方记录失败次数,最后汇总输出"有N个文件被占用,跳过",而不是用失败来打断后面的清理。

4.2 杀毒软件把exe误报了,怎么处理

MinGW静态编译出来的exe,理论上非常干净,但某些杀毒软件会基于"行为特征"判定:一个程序试图批量删除文件,本身就是"病毒行为"。遇到这种误报,常见的处理路径有三个:

  • 代码层面:减少可疑API的调用密度,能合并的合并,删除前加日志输出,调整行为模式。
  • 打包层面:UPX压缩虽然能减小体积,但反而更容易触发杀软脱壳引擎的扫描,非必要不用。
  • 签名层面:申请一个代码签名证书,签名后的exe误报率会大幅下降。

我实际踩过的坑是UPX:为了把300KB压到100KB,结果360、Windows Defender双双拉黑。去掉UPX之后用-s参数,100多KB的体积不压了,反而一点问题没有。

4.3 bat调用中文路径乱码,怎么解决

很多人的实际工作流里,bat只是个启动器。如果bat里写了中文字符串,比如"清理完成",在中文Windows上经常先乱码再出错。根本原因是Windows命令行默认代码页是GBK(936),而源文件保存为UTF-8。解决方法是文件另存为UTF-8 with BOM格式,或者像前面代码里那样,在bat开头加一句chcp 65001 >nul切换到UTF-8代码页。

如果把逻辑全挪到C脚本里,bat只做调用,就没有乱码问题了。因为C源码文件里的字符串编码是编译时定死的,UTF-8中文在Windows控制台里可以通过SetConsoleOutputCP(CP_UTF8)来正常显示,不依赖外部编码设置。

// 在main()开头加一句,保证控制台正确显示UTF-8中文 SetConsoleOutputCP(CP_UTF8);

4.4 内存越界和字符串缓冲区:C脚本的高频事故现场

C脚本虽然用起来爽,但也容易栽在字符串上。我在最初版本里用了不少char path[MAX_PATH],当遍历到深层目录时,路径超过260字节会导致缓冲区溢出。Windows 10支持长路径,但需要注册表开启LongPathsEnabled,同时API要用\\?\前缀。代码层面最稳妥的做法是动态分配路径缓冲区,不要写死大小。

另外一个实用的习惯是:每操作一张路径字符串,都顺手加一个if (strlen(src) >= MAX_PATH) continue;。虽然在工具里有点啰嗦,但能避免半夜清理C盘时突然崩溃的尴尬。

5. 后续扩展的一点建议

这套C脚本的框架一旦跑通,扩展是很自然的事。我现在除了C盘清理,已经把它扩展成了一个小工具集:一个exe支持clean tempclean cacheclean updateclean recycle四个子命令,后续还计划加一个stats模式,专门输出C盘各目录的空间占用统计。再往下走,可以让它自动读取一个clean.ini配置文件,把白名单、保留天数、是否启用回收站清理都写成配置项,这样普通用户不需要碰代码也能自己调整策略。

从"一个C脚本清理C盘"出发,本质上是在示范一种开发方式:当你觉得写脚本语言太飘、写正式工程太重,C语言作为中间一档的"硬核脚本",在系统工具类场景里非常能打。它没有华丽的外壳,但稳定、直接、可控,就像工具箱里的扳手,不显眼但处处都用得上。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 2:46:45

不错的985/211毕业生求职网站 差异化对比及参考

985/211毕业生求职网站的差异化需求 985/211毕业生对求职网站的差异化需求集中在行业垂直性、岗位层级、服务类型、资源类型四个方面&#xff0c;不同需求适配的网站类型完全不同。结合2026年高学历毕业生求职需求调研数据&#xff0c;高学历毕业生的择业诉求普遍偏向高起点、高…

作者头像 李华
网站建设 2026/9/2 2:46:39

PyInstaller打包exe如何还原Python源码:原理、工具与完整实操指南

简介&#xff1a;面向需要还原PyInstaller打包程序源码的开发者与安全分析人员&#xff0c;这款工具包专门解决从exe到py的逆向难题。整套流程自动完成两步核心操作&#xff1a;首先使用内置的pyinstxtractor脚本从可执行文件中提取pyc字节码&#xff0c;随后调用uncompyle6将字…

作者头像 李华
网站建设 2026/9/2 2:46:18

Tesseract OCR在VS2015下编译WIN32动态库,含lib/dll/include完整C++开发库

简介&#xff1a;面向Visual Studio 2015和Windows 32位平台的Tesseract OCR动态库&#xff0c;属于C开发集成包&#xff0c;帮助开发者跳过源码编译&#xff0c;直接嵌入OCR能力&#xff1b;适合桌面工具中的扫描件识别、图片文字提取等场景。压缩包共577个文件、约5.38MB&…

作者头像 李华
网站建设 2026/9/2 2:44:03

STM32F407实现Modbus RTU/TCP网关:FreeRTOS+LWIP+SPI+DMA全解析

简介&#xff1a;面向基于ARM Cortex-M4内核的STM32F407ZET7微控制器开发者&#xff0c;压缩包内是一套整合了轻量化TCP/IP协议栈、开源Modbus协议栈、实时操作系统FreeRTOS、SPI串行外设接口与DMA直接存储器访问驱动的以太网通信工程。整个压缩包共八百三十二个文件&#xff0…

作者头像 李华
网站建设 2026/9/2 2:43:15

本地部署信息差简报生成器:RSS抓取与大模型摘要实战

每天打开手机&#xff0c;热点一个接一个&#xff1a;房贷新政、人形机器人、航天突破、核聚变能、数字产业……但大多数人的动作只是停留在“扫一眼标题”&#xff0c;然后继续刷下一条。真正有用的不是这一条热搜&#xff0c;而是你能不能从一堆零散信息里快速抽出“别人没看…

作者头像 李华
网站建设 2026/9/2 2:42:32

PDR室内定位算法解析:核心步骤、常用算法与工程避坑

简介&#xff1a;面向行人惯性导航&#xff08;PDR&#xff09;研究与开发人群&#xff0c;压缩包内整合了完整的行人航位推算算法实现与配套实测数据。内容覆盖惯性导航系统&#xff08;INS&#xff09;基础、步态检测、步长估算、角度校正、多传感器数据融合及漂移修正等核心…

作者头像 李华