一、当Manjaro告诉你“磁盘已满”
如果你正在使用Manjaro Linux,可能有一天会突然收到系统警告,告诉你家目录或者根目录空间不足了。打开磁盘分析工具一看,发现 /var/cache/pacman/pkg 这个目录占用了好几个G甚至十几G的空间。这时候你可能会疑惑,这些是什么文件?能不能删除?答案是可以的,而且很有必要定期清理。
这些文件是Pacman包管理器在安装和更新软件时留下的缓存。每次你通过 sudo pacman -Syu 更新系统,或者安装一个新软件,Pacman都会先把软件包(一个以.pkg.tar.zst结尾的文件)下载到这个缓存目录里,然后再进行安装。这样设计有个好处:如果你需要重新安装同一个版本的软件,或者给另一台机器安装,Pacman可以直接使用本地缓存,而无需重新下载,节省了时间和流量。
但是,时间一长,特别是对于滚动更新的Manjaro,几乎每周都有更新,旧的软件包缓存就会越积越多。它们就像你电脑里“只进不出”的杂物间,占据了大量宝贵的磁盘空间,尤其是对于使用较小容量固态硬盘的用户来说,这无疑是个头疼的问题。
二、手动清理与自动脚本的对比
面对缓存,我们当然有办法。最简单直接的就是手动清理。
手动清理方法: 你可以使用Pacman自带的一条命令来清理所有未被当前安装的软件引用的旧版本缓存包:
sudo pacman -Sc
这条命令会提示你删除那些“过时”的缓存包。更激进一点,如果你想清空整个缓存目录(包括当前安装版本的缓存,这意味着重装软件需要重新下载),可以使用:
sudo pacman -Scc
手动清理的优点是直接、可控。但缺点也很明显:容易忘记。我们总是要等到磁盘空间告急时才想起来去清理,而且每次都要手动输入命令,对于追求自动化效率的开发者来说,这不够优雅。
于是,自动清理脚本的价值就体现出来了。我们可以编写一个Shell脚本,让它定期(比如每周或每月)自动帮我们执行清理工作,甚至可以制定更智能的规则,比如“只保留最近3个版本的缓存”或者“当缓存超过5GB时自动触发清理”。这样就把我们从重复的劳动和空间焦虑中解放了出来。
三、打造你的专属自动清理脚本
下面,我们就来动手创建一个功能更丰富、更智能的Pacman缓存自动清理脚本。我们将使用Shell脚本(Bash)来实现,因为它是在Linux环境下最直接、最通用的工具。
技术栈:Shell (Bash)
我们将创建一个名为 pacman_cache_cleaner.sh 的脚本。这个脚本将包含以下功能:
- 根据保留的版本数量清理旧包。
- 根据缓存目录总大小是否超过阈值来触发清理。
- 提供“模拟运行”模式,让你先看看哪些文件会被删除,而不实际执行。
- 记录清理日志,方便回溯。
下面是完整的脚本示例,包含了详细的注释:
#!/bin/bash
# ============================================
# Pacman 缓存智能清理脚本
# 功能:自定义规则自动清理 /var/cache/pacman/pkg/
# ============================================
# ---------- 用户配置区域 ----------
# 规则1:保留每个软件包的最新几个版本?
KEEP_LATEST_COUNT=2
# 规则2:当缓存目录总大小超过多少GB时触发清理?
TRIGGER_SIZE_GB=5
# 日志文件存放路径
LOG_FILE="$HOME/.cache/pacman_clean.log"
# 是否启用模拟模式?(true: 只显示要删除的文件,不删除; false: 实际执行删除)
DRY_RUN=false
# ---------- 配置结束 ----------
# 创建日志函数,同时输出到屏幕和日志文件
log_message() {
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$message"
echo "$message" >> "$LOG_FILE"
}
# 检查是否为root用户,pacman清理需要root权限
if [[ $EUID -ne 0 ]]; then
echo "错误:此脚本必须以root权限运行。请使用 sudo 执行。"
exit 1
fi
CACHE_DIR="/var/cache/pacman/pkg"
log_message "=== 开始Pacman缓存检查 ==="
log_message "缓存目录: $CACHE_DIR"
log_message "配置:保留最新 $KEEP_LATEST_COUNT 个版本,触发大小: ${TRIGGER_SIZE_GB}GB"
# 检查触发条件:目录大小
current_size_kb=$(du -sk "$CACHE_DIR" | cut -f1)
current_size_gb=$(echo "scale=2; $current_size_kb / 1024 / 1024" | bc)
log_message "当前缓存目录大小: ${current_size_gb} GB"
if (( $(echo "$current_size_gb < $TRIGGER_SIZE_GB" | bc -l) )); then
log_message "当前缓存大小未超过阈值 ${TRIGGER_SIZE_GB}GB,本次不执行清理。"
log_message "=== 检查结束 ===\n"
exit 0
fi
log_message "缓存大小超过阈值,开始执行清理..."
# 核心清理逻辑:按版本保留数量清理
cleaned_count=0
cleaned_size=0
# 获取缓存目录下所有的 .pkg.tar.zst 文件,并按软件包名分组处理
for pkg_name in $(ls "$CACHE_DIR"/*.pkg.tar.zst 2>/dev/null | xargs -n1 basename | sed 's/-[0-9].*\.pkg\.tar\.zst//' | sort -u); do
# 找出该软件包的所有版本文件,按时间倒序排序(最新的在最前面)
mapfile -t versions < <(ls -t "$CACHE_DIR"/"$pkg_name"-*.pkg.tar.zst 2>/dev/null)
total_versions=${#versions[@]}
# 计算需要删除的旧版本数量
to_remove_count=$((total_versions - KEEP_LATEST_COUNT))
if (( to_remove_count > 0 )); then
log_message "软件包 '$pkg_name' 共有 $total_versions 个版本,将删除最旧的 $to_remove_count 个。"
# 从列表后面开始取(因为列表已按时间倒序,后面的是最旧的)
for (( i=total_versions-1; i>=KEEP_LATEST_COUNT; i-- )); do
file_to_remove="${versions[$i]}"
file_size=$(stat -c%s "$file_to_remove" 2>/dev/null)
if [[ "$DRY_RUN" == true ]]; then
log_message "[模拟] 将删除: $(basename "$file_to_remove") (大小: $((file_size/1024/1024))MB)"
else
log_message "正在删除: $(basename "$file_to_remove")"
rm -f "$file_to_remove"
# 如果删除成功,更新统计
if [[ ! -f "$file_to_remove" ]]; then
((cleaned_count++))
cleaned_size=$((cleaned_size + file_size))
fi
fi
done
fi
done
if [[ "$DRY_RUN" == true ]]; then
log_message "[模拟模式] 已预览清理操作,未实际删除任何文件。"
else
cleaned_size_gb=$(echo "scale=2; $cleaned_size / 1024 / 1024 / 1024" | bc)
log_message "清理完成!共删除 $cleaned_count 个旧缓存包,释放空间约 ${cleaned_size_gb} GB。"
fi
log_message "=== 清理操作结束 ===\n"
如何使用这个脚本:
- 将上面的代码保存到一个文件中,例如
~/scripts/pacman_cache_cleaner.sh。 - 给脚本添加执行权限:
chmod +x ~/scripts/pacman_cache_cleaner.sh。 - (重要) 修改脚本开头的用户配置区域。你可以把
KEEP_LATEST_COUNT改成你想保留的版本数(比如3),把TRIGGER_SIZE_GB改成你希望的触发大小(比如10)。初次运行建议将DRY_RUN设为true,先看看效果。 - 使用
sudo运行脚本进行测试:sudo ~/scripts/pacman_cache_cleaner.sh。在模拟模式下,它只会列出将要删除的文件。 - 确认无误后,将
DRY_RUN改为false,再次运行即可真正清理。 - 最后,我们可以通过系统的
cron服务来定期自动执行这个脚本。例如,编辑root用户的cron任务表(sudo crontab -e),添加一行,让它每周日凌晨3点运行:
这样,每周它都会自动检查并清理,你完全不用再操心磁盘空间问题了。0 3 * * 0 /home/你的用户名/scripts/pacman_cache_cleaner.sh
四、深入理解:Pacman缓存机制与关联技术
我们的脚本核心逻辑是围绕Pacman缓存文件的特点展开的。理解这些,有助于你修改或编写更适合自己的规则。
Pacman缓存文件命名规则:
一个典型的缓存文件名看起来像这样:firefox-115.0.3-1-x86_64.pkg.tar.zst
firefox: 软件包名称。115.0.3-1: 软件包的完整版本号(上游版本-打包版本)。x86_64: 架构。.pkg.tar.zst: 扩展名,表明这是一个用Zstandard压缩的tar归档包。
我们的脚本利用了这个命名规律。通过 sed 命令 sed 's/-[0-9].*\.pkg\.tar\.zst//',我们轻松地从文件名中提取出纯粹的软件包名(如 firefox),从而能够对同一个软件包的不同版本进行分组。
关联技术:Shell脚本中的数组与循环
脚本中使用了Bash的数组(versions)来存储一个软件包的所有版本文件路径。mapfile -t versions < <(...) 这个技巧可以将命令输出的每一行直接读入数组的一个元素中,非常高效。
随后的 for (( i=total_versions-1; i>=KEEP_LATEST_COUNT; i-- )) 循环,是一个经典的C语言风格的for循环,它让我们能够精确地从数组的末尾(最旧的版本)开始向前删除,只保留最新的指定数量(KEEP_LATEST_COUNT)的文件。这种精确控制是单纯使用 pacman -Sc 命令所不具备的。
五、应用场景、优缺点与注意事项
应用场景:
- Manjaro/Arch Linux日常用户:尤其是使用小容量SSD的用户,是此脚本最直接的需求者。
- 多系统维护者:如果你管理着多台Manjaro设备,可以在每台设备上部署此脚本,实现统一的缓存管理策略。
- 追求自动化的开发者:将系统维护任务脚本化、自动化,是DevOps精神的体现,能节省宝贵的时间和注意力。
技术优缺点:
- 优点:
- 高度自定义:你可以自由定义保留版本数和触发清理的磁盘大小,比Pacman原生命令更灵活。
- 自动化:结合cron,实现全自动维护,一劳永逸。
- 安全可控:提供模拟运行模式,避免误操作。详细的日志记录了每一次操作,便于审计和排查问题。
- 资源节约:有效释放磁盘空间,避免因空间不足导致系统或应用程序出错。
- 缺点:
- 需要root权限:清理系统缓存必须提升权限,脚本本身需要安全存放和执行。
- 规则设计风险:如果
KEEP_LATEST_COUNT设置过小(如0或1),在需要降级软件包时可能会因为找不到旧版本缓存而失败。虽然这种情况很少,但需要留意。 - 依赖命名规则:脚本逻辑依赖于Pacman包的文件名格式。如果未来Pacman更改了命名约定,脚本可能需要调整。
重要注意事项:
- 首次务必模拟运行:在将
DRY_RUN设置为false之前,一定要先以模拟模式运行一次,仔细查看日志,确认将要删除的文件符合你的预期。 - 谨慎设置保留数量:对于系统核心组件(如
linux内核、nvidia驱动),建议多保留1-2个版本,以便在出新版本遇到问题时可以快速回退。你甚至可以修改脚本,为特定包设置不同的保留策略。 - 备份与降级考量:清理掉的旧版本缓存无法用于
pacman -U本地安装或降级。如果你有软件降级的强烈需求,请确保相关包的缓存已被妥善保留或备份。 - 日志管理:脚本会不断向日志文件追加内容,记得定期检查或清理旧的日志文件,防止日志本身占用过多空间。
六、总结
磁盘空间管理是Linux系统维护中一个虽小却不可忽视的环节。通过本文,我们不仅解决了Manjaro中Pacman缓存占用空间的问题,更掌握了一种将重复性管理工作自动化、智能化的思路。
我们从手动清理的痛点出发,一步步设计并实现了一个功能完善的自动清理脚本。这个脚本不仅包含了按版本数量清理的核心算法,还增加了磁盘空间阈值触发、模拟运行和安全日志等实用功能。更重要的是,我们将其与系统的cron计划任务结合,实现了真正的“设置后不管”。
希望这个脚本和其中蕴含的思路,能帮助你更好地管理你的Manjaro系统,让你能将更多精力投入到创造性的工作和学习中去。毕竟,让机器为我们处理这些琐事,正是技术进步带给我们的便利之一。
评论