前阵子一个小兄弟半夜给我发消息,说测试服务器的磁盘又满了,整个环境上的 Jenkins 都挂了,构建任务全积压。我上去一看,/tmp底下堆了几十个 G 的构建残留,~/.gradle缓存快 20G,docker 的悬空镜像也占了一堆。说实话这种场面我见过太多次了,开发机、测试机、生产环境,几乎每个系统都会被临时文件“温水煮青蛙”式地拖垮。今天就把我这几年整理出来的临时文件自动化管理方案完整地讲一讲,从产生机制到工具选型,再到能直接抄的脚本和定时任务,最后还有一堆实际踩过的坑,希望能帮你少走点弯路。
这套方案适合谁?如果你是开发、运维、测试,或者手头管着一台以上机器的“兼职网管”,又或者只是觉得自己电脑 C 盘总在莫名其妙变小,都可以照着我这套思路去落地。它不依赖某个特定平台,Linux、Windows、macOS 都有对应的玩法,核心思路是一样的:把清理动作从“想起来才做”变成“系统自动做”,并且让每一次删除都可审计、可回溯。
1. 临时文件为何失控:从产生机制到管理难点
1.1 临时文件的几种典型来源与生命周期分析
很多人一想到临时文件,脑子里就只有一个C:\Users\xxx\AppData\Local\Temp或者/tmp,但真正把磁盘吃掉的,往往是那些“披着业务外衣的临时文件”。我粗略盘了一下,至少有以下几大类:
- 构建与包管理缓存:
npm cache、pip cache、~/.gradle、~/.m2/repository、go pkg mod缓存等。这类目录增长极其疯狂,尤其是前端项目,装一次依赖就能堆几个 G。 - 操作系统临时目录:就是我们熟知的
/tmp、/var/tmp、%TEMP%。很多程序运行时不优雅退出,临时文件就留在那儿了,而且权限常常是sticky bit,普通用户删不掉别人的残余文件。 - 日志与崩溃转储:
/var/log、app.log.1、.log.2.gz、Windows 下的Windows\Minidump。日志滚动策略没配置好,一个空转的进程就能在几个月内生成几十 G 日志。 - 容器与镜像残留:
docker悬空镜像、构建缓存、overlay2里被删除但未回收的层数据。 - 应用自身缓存:Electron 应用的
Cache目录、浏览器缓存、输入法 / 杀毒软件的工作目录。
这些文件有一个共同特征:生命周期理应很短。程序本意是“用完即走”,但操作系统不强制回收,程序崩溃了更不会管,于是它们的生命周期从“几分钟”变成了“永远”。最后磁盘告警了,才有冤大头(通常是我这样的角色)被拉过去手工清。
1.2 手动清理为什么治标不治本
手动清理最大的问题不是“累”,而是不可控。我们来看看一次典型的手动清理会发生什么:
- 你凭经验打开几个大目录,
du -sh扫一遍,选几个最肥的下手。 - 你担心误删,所以只敢删那些看起来明显没用的(比如带
.tmp、.log后缀的文件)。 - 删除后空间释放了,你心满意足地关掉终端。
- 两周后,同样的问题再次出现,你重复一遍以上操作。
这个循环里藏着三个致命伤:第一,你的清理准则是“拍脑袋”式的,今天删了这几个目录,明天换了个软件生产新垃圾,你根本不知道;第二,手动命令一旦输错路径、写错通配符,就是大规模误删事故,我见过有人把rm -rf ./dist打成rm -rf / dist的操作(中间那个空格,懂的都懂);第三,手动清理没有任何审计记录,删了什么、删了多少、有没有删错,全凭记忆,而记忆是最不可靠的。
1.3 自动化管理的核心需求清单
做自动化之前,先要明确我们需要什么能力,不然写着写着就变成“一个不断往生产环境扔 rm -rf 的定时炸弹”。我自己的需求清单一直是这五条:
- 不误删:必须有明确的保留策略、白名单和黑名单。宁可漏删,不可错删。
- 可观测:删除前后要有记录,日志要能查到。否则出了问题连锅都甩不明白。
- 可恢复:至少你知道删掉的是什么。严格一点可以搞回收站方案,但大部分场景下“有日志、有列表”就够用了。
- 低维护:规则一旦上线,应该能稳定跑几个月不需要人干预。
- 跨平台:如果你像我一样同时管着 Windows 笔记本和 Linux 服务器,最好有一套通用思路,而不是每台机器都写一套完全不同的逻辑。
2. 自动化方案选型:脚本、系统工具与第三方程序的取舍
2.1 原生系统工具:零依赖的第一选择
很多人一上来就想着写脚本,但原生工具往往已经覆盖了 80% 的基础需求,而且稳定性经过大量验证,优先用它们永远是对的。
Linux上最值得说的是systemd-tmpfiles。这个工具虽然名字带 tmpfiles,但它不只是管/tmp,任何你想定期清理的文件都能通过/usr/lib/tmpfiles.d/*.conf或/etc/tmpfiles.d/*.conf来交给它管。例如在配置文件里写:
d /tmp/example 1777 root root 7d D /var/cache/nginx 0755 nginx nginx 30d第一行表示/tmp/example目录保留 7 天内的文件并定期清理超龄的;第二行强制清理/var/cache/nginx下所有超过 30 天的内容。它最大的优势是系统级守护,只要 systemd 活着它就在,不需要额外写 cron。缺点是配置语法需要学习,而且D和d的区别容易记混,后面实操部分我会专门讲坑。
Windows上对应的是“存储感知”(Storage Sense)功能。在“设置 → 系统 → 存储”里开启后,可以设置“删除临时文件”的频率,系统后台会自动清理%TEMP%、回收站和旧版 Windows 更新文件。它的问题是清理范围受限,管不了应用自身的缓存目录(比如%LOCALAPPDATA%\Google\Chrome\User Data\Default\Cache)。所以 Windows 我一般还是推荐“存储感知 + 计划任务 + PowerShell 脚本”的混合方案。
2.2 脚本化方案:find、cron 它们才是自由度的天花板
原生工具够用,但不够“自由”。当你需要“删除所有大于 100MB 的视频缓存文件”“清理访问时间超过 7 天的构建产物”“排除正在被进程使用的日志文件”这类精细规则时,就得靠脚本了。
Linux 下最核心的命令就是find。一条最基本的清理命令长这样:
find /tmp -type f -atime +7 -delete意思是:在/tmp下找普通文件(-type f),访问时间(-atime,即 access time)超过 7 天的,直接删掉。这里出现过无数新人会踩的坑:把-atime写成-mtime。两者差别巨大,下面专门讲。
| 参数 | 含义 | 典型用途 |
|---|---|---|
-atime | 最后访问时间(access time) | 清理“很久没人看过的文件” |
-mtime | 最后修改时间(modification time) | 清理“很久没改过的文件” |
-ctime | 元数据变更时间(change time) | 排查权限/属性改动 |
-size | 按文件大小过滤 | 优先清理大文件 |
-type f | 只匹配普通文件 | 避免误删目录/链接/设备文件 |
-prune | 剪枝,排除某些子树 | 跳过正在使用的目录 |
访问时间和修改时间的区别非常关键。比如一个build/目录下的产物,你上次访问它可能是 10 天前,但它的修改时间(内容写入时间)也还是 10 天前,两者区分不大;但如果你在日志目录下grep、tail过某个文件,它的atime就会被刷新到“刚刚”,这样的文件用-atime +7就永远清不掉。反过来,有些缓存文件被程序主动修改过但再没读过,-mtime +7就合适。我自己的经验是:清理临时文件优先看-atime,因为“最后一次被使用”更贴近“它还有没有用”;清理构建产物看-mtime,因为构建产物不看内容、只看生成时间。
2.3 第三方专业工具与自研小工具:补充而非主力
第三方图形化工具,比如 Windows 的 CCleaner、跨平台的 BleachBit,界面友好,一键清理,适合个人电脑。但拿来做自动化我有顾虑:它们大多不是为无人值守设计的,而且黑盒操作多,你不知道它到底动了哪些文件。在服务器和开发环境里,我更倾向自研小工具或用命令行工具配合。
这里值得推荐的是磁盘占用分析工具:Linux/macOS 用ncdu,Windows 用WizTree或TreeSize。它们的定位不是自动清理,而是摸家底——搞清楚空间到底被谁占了,自动化脚本的黑白名单和目录清单从哪来?就靠这些扫描工具来定。自动清理交给自研脚本后,第三方工具就退居成“周期巡检时的人工审计工具”。
2.4 工具选型对比表
| 方案 | 可控性 | 学习成本 | 灵活性 | 适用场景 |
|---|---|---|---|---|
| systemd-tmpfiles | 中 | 中 | 中 | Linux 发行版自带的、定期清理固定目录 |
| 存储感知 (Windows) | 低 | 低 | 低 | 个人 Windows 电脑的日常清灰 |
| find + cron 脚本 | 高 | 中 | 高 | 服务器、开发机、自定义规则多的场景 |
| 第三方清理工具 | 低 | 低 | 低 | 个人电脑的交互式“一键清理” |
| 自研清理服务 | 最高 | 高 | 最高 | 大规模集群、需要审计与告警的生产环境 |
我见过的大多数团队,最后都收敛到“find + cron 或 systemd timer 为主,systemd-tmpfiles 兜底,人工巡检工具辅助”的组合。下面这套实操流程,就是基于这个组合展开的。
3. 实操实现:一套可以照抄的临时文件自动化清理方案
3.1 需求规划:先盘家底,再定规则
动手写脚本之前,先用 15 分钟把机器上每一个“疑似垃圾产生源”的目录过一遍:
du -sh ~/.gradle ~/.npm ~/.cache /var/log /tmp /var/tmp 2>/dev/null | sort -rhWindows 下可以打开 PowerShell:
Get-ChildItem "$env:LOCALAPPDATA","$env:TEMP","C:\Windows\Temp" -Directory | ForEach-Object { $size = (Get-ChildItem $_.FullName -Recurse -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum [PSCustomObject]@{ Path = $_.FullName; SizeMB = [math]::Round($size/1MB,1) } } | Sort-Object SizeMB -Descending这一步的目标是输出一张目录清单,每一项标注:路径、预估大小、文件类型、谁在写、能不能删。以我常用的测试服务器为例,最终可能长这样:
| 路径 | 大小 | 类型 | 保留策略 |
|---|---|---|---|
/tmp | 30G+ | 构建残留、临时解压 | -atime +7删除 |
/var/log | 8G | 滚动日志 | 保留 30 天,压缩后删除 |
~/.gradle/caches | 20G | 依赖缓存 | 保留 30 天内修改的 |
~/.cache/pip | 3G | 下载缓存 | 直接清空,需要时重下 |
docker悬空镜像 | 15G | 镜像层 | docker image prune -f |
有了这张表,写脚本就是填参数的事。这里我给你一个非常关键的提醒:清理规则的粒度要细到“目录 + 文件类型 + 时间”三个维度,任何缺失都会出问题。比如只写“清理/tmp下所有 7 天前的文件”,后面一旦有服务长期占用/tmp/httpd.sock,你这条规则就会跟线上服务干架。
3.2 脚本核心实现与参数解读
先给一个 Linux 下的 bash 脚本模板,我给它取名叫tmp_cleaner.sh。定位是“可预览、可执行、可审计由同一条命令完成”:
#!/usr/bin/env bash set -euo pipefail # ---------- 配置区 ---------- LOG_BASE="/var/log/tmpcleaner" RUN_DATE="$(date +%Y%m%d_%H%M%S)" LOG_FILE="${LOG_BASE}/clean_${RUN_DATE}.log" DRY_RUN="${DRY_RUN:-false}" # 设为 true 时只打印不删除 # 目录与保留策略: 路径 | 保留天数 | 文件过滤条件 declare -a RULES=( "/tmp|7|-type f" "/var/tmp|7|-type f" "/var/log|30|*.log.*" "${HOME}/.cache/pip|0|-type f -mtime +1" ) # ---------- 函数区 ---------- log_info() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG_FILE" } clean_dir() { local target="$1" days="$2" filecond="$3" if [ ! -d "$target" ]; then log_info "SKIP: $target 不存在" return 0 fi if [ "$DRY_RUN" = "true" ]; then log_info "DRY-RUN 列出 $target 下超过 ${days} 天的文件:" find "$target" -type f -mtime "+${days}" $filecond -print | head -n 50 else local before after before="$(du -sb "$target" | cut -f1)" find "$target" -type f -mtime "+${days}" $filecond -delete after="$(du -sb "$target" | cut -f1)" log_info "CLEAN $target 释放空间: $(( (before - after) / 1024 / 1024 )) MB" fi } # ---------- 主流程 ---------- mkdir -p "$LOG_BASE" log_info "===== TMP CLEAN START =====" for rule in "${RULES[@]}"; do IFS='|' read -r dir days cond <<< "$rule" log_info "处理规则: $dir / $days天 / $cond" clean_dir "$dir" "$days" "$cond" done log_info "===== TMP CLEAN END ====="这个脚本有几个地方我特意加了“防呆”设计,值得单独解释:
第一处是set -euo pipefail。-e让脚本在遇到第一条错误时立即退出;-u让未定义变量的引用直接报错;-o pipefail让管道命令的失败不静默。临时文件清理领域最怕的就是“半成功半失败你还不知道”,这三件套是脚本保命符。
第二处是DRY_RUN环境变量。DRY_RUN=true ./tmp_cleaner.sh可以在不删除任何文件的情况下,打印出会被清理的文件清单。我强烈建议任何清理脚本都内置这个开关,上线第一周先跑干跑模式,输出进入日志后再人工抽查一遍,确认没有误伤再打开实际删除。
第三处是日期字段的格式。日志文件带上RUN_DATE,每次清理都是一个独立文件,后面排查“上周五删了什么”的时候,不需要去一个巨大的日志文件里 search,直接ls就能定位。
Windows 下我用 PowerShell 写过一个等价脚本,核心清理语句是:
$limit = (Get-Date).AddDays(-7) Get-ChildItem $env:TEMP -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt $limit } | Remove-Item -Force -ErrorAction SilentlyContinue注意$env:TEMP下的文件可能正被别的程序使用,Remove-Item即使加了-ErrorAction SilentlyContinue也会把错误吞掉,所以 Windows 下一定要在脚本外用“事务 / 日志”思路记录枚举列表,不然删了没删你都不知道。
3.3 定时调度配置:cron 与任务计划程序的正确姿势
脚本写好了,接下来把它挂上定时。Linux 下最直接的是写 crontab。执行crontab -e,加规则:
0 2 * * * DRY_RUN=false /opt/scripts/tmp_cleaner.sh >> /var/log/tmpcleaner/cron.log 2>&1这里有几个坑我逐一踩过,你直接避开:
- cron 环境变量极度精简,PATH 默认只有
/usr/bin:/bin。如果你的脚本用到了/usr/local/bin下的命令(比如jq、docker),要么在脚本里全用绝对路径,要么在 crontab 顶部加一行PATH=/usr/local/bin:/usr/bin:/bin。我见过太多“手动执行没问题,crontab 里跑不起来”的案例,99% 是 PATH 问题。 - cron 不会主动满足脚本依赖的目录权限。如果你的脚本写日志到
/var/log/tmpcleaner,但该目录的属主是root,而 crontab 是普通用户安装的,那执行时mkdir -p会失败、tee会失败。建议日志目录归属当前用户,或者申请 root 权限的 crontab 任务。 - 定期任务要做好防重入。如果清理任务跑太久(比如构建缓存目录巨大),下一次 cron 触发时上一次还没结束,两个实例同时 find 一个大目录,轻则负载升高,重则重复删除引发资源竞争。解决方案在 cron 调用前加
flock:
0 2 * * * /usr/bin/flock -n /var/run/tmpclean.lock -c "/opt/scripts/tmp_cleaner.sh" >> /var/log/tmpcleaner/cron.log 2>&1如果上一次任务没结束,flock -n会直接放弃本次调度,宁可不跑也别并发跑。
不想用 crontab 的话,systemd timer 是更现代的替代。先写一个 service 文件/etc/systemd/system/tmpcleaner.service:
[Unit] Description=Temporary File Cleaner Service [Service] Type=oneshot ExecStart=/opt/scripts/tmp_cleaner.sh再写对应的 timer 文件:
[Unit] Description=Run Temporary File Cleaner Daily [Timer] OnCalendar=*-*-* 02:30:00 Persistent=true [Install] WantedBy=timers.target然后执行systemctl daemon-reload && systemctl enable --now tmpcleaner.timer即可。Persistent=true的意义在于:即使机器在那个时间点刚好关机,下次开机后它也会补跑任务,避免“停机错过清理窗口”的空窗期。
Windows 下则在“任务计划程序”里新建任务:触发器设为“每天凌晨 2 点”,操作设为“启动程序”,程序填powershell.exe,参数填-WindowStyle Hidden -ExecutionPolicy Bypass -File C:\Scripts\tmp_cleaner.ps1。记住两个容易踩的选项:一是“条件”标签页的“只有在计算机使用交流电源时才启动此任务”要取消,否则笔记本没插电时永远不跑;二是“设置”标签页的“如果任务运行时间超过以下时间,停止任务”要留一个 30 分钟到 1 小时的余量,防止脚本卡死。
3.4 日志审计与磁盘监控告警
清理脚本跑起来只是第一步,第二步是确认它跑得安全、跑得有效。日志层面,我习惯做两件事:
- 每一次删除都留下“清单”。删了多少个文件、每个文件多大、总共释放多少空间,务必落盘。上面脚本里用
du -sb $target前后对比,其实不太精确(目录里有并发写入时误差很大),更可靠的做法是:find时先输出列表到cleaned_$RUN_DATE.lst,然后用wc -l统计文件数,再归档。 - 删除总量与磁盘水位写入状态监控。最简单的方式是脚本结束后把
df -h /的当前值写进日志,方便回溯。如果有 Zabbix、Prometheus 一类的监控系统,写一个文件disk_percent让 agent 采集,或直接脚本末尾调 API 上报,都行。
磁盘水位告警我强烈建议独立于清理脚本做,因为清理本身有失败的可能,不能“以清代监”。我自己常用的告警脚本参数如下:
threshold=80 current=$(df / | awk 'NR==2 {print $5}' | tr -d '%') if [ "$current" -gt "$threshold" ]; then echo "磁盘使用率已超过 ${threshold}%: ${current}%" | mail -s "Disk Alert $(hostname)" ops@example.com fi如果你是个人电脑用户,没有监控系统,也可以让清理脚本每周末把摘要输出到桌面上的一个文本文件,至少做到“空间变化有迹可循”。有时候你发现有台机器磁盘涨得异常,就是因为某个清理任务被静默失败了两周,而没人注意到水位一直在涨。
4. 典型故障与排查技巧实录
4.1 删了日志,空间却不释放的诡异问题
有次我在生产环境删掉了一个 12G 的日志文件。ls -lh显示文件明明没了,但df -h一看,根分区使用率纹丝不动。当时第一反应是“我删错目录了”,反复确认路径没问题。后来用lsof | grep deleted一查,480 多行的输出里,几十个进程持有被删除文件的文件句柄。Linux 下,只要有一个进程还握着已删除文件的口子,那部分磁盘空间就一直被占着,直到进程关闭句柄或退出。
这个坑的教训是:清理日志类文件,优先用日志滚动(logrotate)而不是直接rm。logrotate 会先让进程重新打开新日志文件,然后再删除旧文件,进程没有句柄在旧文件上,空间才会真正释放。如果已经中了招,就只能重启持有这些句柄的进程,不过生产环境重启前务必先确认进程的优雅退出机制,免得数据丢失。
Windows 下等价问题是文件被 Explorer 或杀毒软件锁定,Remove-Item抛 “being used by another process”,这个不需要太纠结,换个角度:先看openfiles或使用 Sysinternals 的 handle 工具定位锁文件的进程,再决定是重启应用还是直接跳过该文件。
4.2 误删正在写入的临时文件,程序直接崩了
有次我为了图快,直接用find /app/tmp -type f -mtime +0 -delete,结果把一个正在监听 UNIX socket 的中间件给干崩了——它启动时创建的.sock文件被当成“无用的临时文件”清掉,然后新请求进来时,程序发现 socket 路径不在了,直接抛错拒连。
这类错误的本质是:程序运行期的临时文件,生命周期不等于“闲置时间”,而等于“进程存活时间”。.sock、.lock、.pid这些文件,即使几个月都没人碰过,也不能被自动清理删掉,因为它们是进程间的通信或互斥信号。
我现在处理这种目录的方法是:在清理规则里明确排除,不盲目全清。比如使用find时配合-name排除:
find /app/tmp -type f -mtime +7 -delete \ ! -name '*.sock' \ ! -name '*.lock' \ ! -name '*.pid'或者更保险一点,干脆只清理明确已知后缀的临时文件,比如*.tmp、*.tmp.*、*.part、*.download,其他一律不碰。加白名单永远比加黑名单安全,因为黑名单是“默认删除、个别排除”,只要有一项遗漏就是事故;白名单是“默认不删、指定才删”,最坏情况只是清得不彻底,不会出大乱子。
4.3 Docker 悬空镜像清理带来的现网故障
容器环境里跑docker image prune -f或者docker system prune -af之前,我心里要过一遍:当前运行中的容器是否在引用某一个“老但没打 tag”的镜像层。大多数情况下 prune 只删除悬空的、没有被任何容器引用的镜像,是安全的,但如果你用了“通过镜像 ID 启动容器”这种方式,镜像可能没有 tag,其实并不悬空——这种边缘情况,prune 会直接把你正在跑的镜像块删掉,容器启动时会报 “No such image” 或者回滚异常。
我的经验是两步走:第一步先干跑docker image ls -f dangling=true看看清单,确认没有正在使用的;第二步再配合docker ps -a检查容器的镜像 ID 是否在清单里出现。自动化脚本里我通常只跑悬空清理,不开-a全量清理,因为构建缓存、未使用的镜像虽然有价值,但换来一次误删事故的代价太高。
4.4 把临时文件放到内存盘:一个“一劳永逸”的思路
如果你实在受够了“定期清理”这套戏码,还有一种思路是釜底抽薪:把某些临时目录直接挂载成 tmpfs(内存文件系统)。Linux 下改/etc/fstab,加入一行:
tmpfs /tmp tmpfs defaults,noatime,size=4G,mode=1777 0 0重启后/tmp就变成最多 4G 的内存盘,系统一重启全部清空,“临时文件永远存在”的问题直接不存在。缺点也很明显:内存容量有限、大文件存储会导致物理内存压力;如果某个程序往/tmp写了大量数据,反而更容易触发 OOM。所以这个方案我一般只推荐给“构建任务多但都不是超大文件”的开发机,生产环境还是老老实实用磁盘清理。
4.5 快速排障速查表
无论多仔细,自动化清理总会出这样那样的问题。下面这张表我把这几年遇到的高频故障和排查路径整理成了速查形式,建议收藏:
| 症状 | 最可能的原因 | 排查命令 / 动作 |
|---|---|---|
| 文件删了,磁盘空间没降 | 进程持有已删除文件的句柄 | lsof +L1定位,重启相关进程 |
| 定时任务没跑 | cron 环境 PATH 不全 / 权限不足 | 检查 crontab 日志,脚本内加日志 |
| 清理脚本跳过某目录 | 路径拼写 / 正则不匹配 | 先find -print确认为空再怀疑脚本 |
| 清理导致服务异常 | 误删.sock/.lock/.pid | 看服务日志中 socket 路径引用,改白名单排除 |
| Docker 镜像清理后启动失败 | 镜像没有 tag 但被引用 | docker image ls -f dangling=true对比容器镜像 ID |
| Windows 清理时文件被锁 | 应用进程占用 | 用 handle 工具找到锁进程,跳过该文件 |
| 磁盘依然缓慢增长 | 清理范围未覆盖某缓存目录 | 用 du 扫描 TOP N 目录,补充规则 |
写在最后的实操心得
我自己的习惯是把这套清理方案当成“小步迭代的工程”来对待:第一周先只清一个目录,开着DRY_RUN跑三天;第二周加第二个目录,从预览模式切到实际删除;第三周再加日志和告警。一个月后规则稳定了,基本就是纯收租状态。另外有个小技巧是日志文件进行按月轮转:/var/log/tmpcleaner/下每个月一个子目录,配合tar打个包,既方便回溯也不占空间,不然清理脚本自己的日志也会变成新的“临时文件”,那就荒诞了。
最后再提醒一句:清理脚本的find条件别写太激进。宁可让它漏清一些,也别让它误删一个正在被使用的文件。空间没了还能腾挪,数据没了就真的追不回来了。祝你在这套方案基础上,搭出自己的自动化清理体系,早点结束“半夜被磁盘告警叫醒”的日子。