如何清理var/cache/apt,以便只保留每个软件包的最新版本。

我想知道一种清理var/cache/apt文件夹的方法,只保留最新版本的软件包,如果有多个版本,则只保留一个程序的软件包。
例如,我有几个vlc软件包(vlc_1.1.11,vlc_1.1.12..)和几个wine软件包(wine1.3_1.3.34,wine1.3_1.3.35,wine1.3_1.3.36,wine1.3_1.3.37...)以及其他类似的软件包。
所以如何在这个文件夹中进行清理,只保留最新的软件包。目前我有2.5GB的空间,其中大部分只是旧版本的软件包与新版本的混合在一起。
4个回答

使用apt-get或aptitude的autoclean选项。
sudo apt-get autoclean
sudo aptitude autoclean

从man页面
清理
clean命令会清空本地存储库中已获取的软件包文件。它会从/var/cache/apt/archives/和/var/cache/apt/archives/partial/目录中删除除了锁文件以外的所有内容。
自动清理
与clean命令类似,autoclean命令也会清空本地存储库中已获取的软件包文件。不同之处在于,它只会删除无法再次下载且基本上没有用处的软件包文件。这样可以在长时间内保持缓存而不让其失控增长。

你能添加sudo aptitude autoclean吗?同时apt-get和aptitude的autoclean都会保留较大的版本跳跃,例如Thunderbird 8.0和Thunderbird 9.0。它确实删除了Thunderbird 8.x的旧软件包,但是它保留了完整发布版本,而没有考虑到9.0比8.0更高。这只是我还有很多类似情况之一。还有其他方法来处理这么多旧软件包吗? - Luis Alvarado
很抱歉,如果自动清洁没有起作用,我不知道有什么简单的方法。希望其他人能提供更好的建议。 - Panther

我提议以下的bash脚本
#!/bin/bash

cd /var/cache/apt/archives/
printf '%s\n' *.deb | 
  awk -F_ '{ print $1 }' |
  sort -u | 
  while read pkg; do 
    pkg_files=($(ls -t "$pkg"_*.deb))
    nr=${#pkg_files[@]}
    if ((nr > 1)); then
      unset pkg_files[0]
      echo rm "${pkg_files[@]}"
    fi
  done

rm行中删除echo,如果您对输出列表满意。
它是做什么的?
1. 列出所有deb软件包文件 2. 从第一个"_"到末尾删除文件名中的所有内容,获取软件包名称 3. 对名称进行排序,删除重复项 4. 对于每个名称 - 按时间顺序列出与该名称对应的软件包文件 - 计算列表中的软件包文件数量 - 如果列表中有多个软件包 - 从列表中删除第一个和最新的文件 - 从磁盘中删除与该名称对应的所有其他文件
通过仅列出从sortsort -u之间的差异获得的软件包名称对应的软件包文件,可以提高效率。

它完美地运行了。我也给bodhi加了一个+1,因为自动清理也帮助了一点清理工作。所以也许两者一起做得很好。谢谢enzotib。 - Luis Alvarado

需要强调的是,无论是apt-get autoclean还是aptitude autoclean都不会删除旧版本,而是删除不再存在于存储库中的过时软件包。
此外,其他答案中提出的脚本忽略了软件包的架构,这意味着如果您有不同架构的软件包,只会保留一个并删除其他软件包。
换句话说,没有任何可用的答案提供全面的解决方案。因此,这里提供一条命令行作为其他答案的替代方案:
$ basename -a /var/cache/apt/archives/*.deb | cut -d _ -s -f 1,3 --output-delimiter=_*_ | uniq -d | xargs -I{} sh -c "find /var/cache/apt/archives/ -maxdepth 1 -name {} -print | sort -V | head -n -1" | xargs -r sudo rm

解释

basename -a /var/cache/apt/archives/*.deb

列出所有文件名,但不包括完整路径。

cut -d _ -s -f 1,3 --output-delimiter=_*_

使用星号(*)替换软件包版本,保留基本软件包名称(前缀)和其体系结构(后缀)。

uniq -d

删除重复条目并同时删除没有重复的条目。
xargs -I{} sh -c

对于每个条目,执行引号中的命令,并用输入文件掩码替换{}模式。
find /var/cache/apt/archives/ -maxdepth 1 -name {} -print

在存档目录中执行非递归搜索以查找目标模式,并将结果打印到标准输出。
sort -V

每次搜索都会对结果进行排序。
head -n -1

删除最后一个结果,即具有数值最高版本的结果(其他结果将在最后被移除,但最后一个结果将保留)。
xargs -r sudo rm

将来自所有搜索的结果放在一起,并将它们传递给以超级用户权限执行的删除命令。

好的,谢谢。 - Jags
嗨 @trudy,我刚刚尝试了修改后的命令,但它甚至没有要求输入sudo密码。光标只是跳到下一行/提示符。我不确定是否漏掉了什么。我尝试的命令是:basename -a /var/cache/apt/archives/*.deb | cut -d _ -s -f 1,3 --output-delimiter=_*_ | uniq -d | xargs -I{} sh -c "find /var/cache/apt/archives/ -maxdepth 1 -name {} -print | sort -n | head -n -1" | xargs -r sudo rm。谢谢。 - Jags
@Jags,没错。在xargs命令中添加-r标志可以防止执行命令,如果传入的参数列表为空(详见man xargs了解更多信息)。如果系统中没有要删除的内容,那么sudo rm命令就不会被执行。这就是为什么你没有被要求输入root密码的原因。如果需要进行故障排除,你可以移除最后一个管道符号| xargs -r sudo rm。然后该命令将只打印出待删除的软件包列表。在你的情况下,它将不会打印任何内容。 - Trudy
非常感谢 @trudy 的解释,目前确实没有需要删除的内容。 - Jags

我提议一个更简单、更易读的bash脚本,基于enzotib's算法,不像原始版本那样删除额外的文件。
#! /usr/bin/env bash

cd /var/cache/apt/archives/
for pkg in `ls *.deb | cut -d _ -f 1 | sort -u`; do
    if [ $(ls $pkg\_* | wc -l) -gt 1 ]; then
        files=$(ls -vr $pkg\_*)
        rmfiles=`echo $files | cut -d " " -f 2-`
        rm -v $rmfiles
    fi
done