问题背景
archlinux 是个可高度定制的 linux 发行版,在使用的过程中,需要反复测试很多软件包的功能,以达到自己想要的效果,效果不好的卸载,用得好的留下,但由于依赖是复杂的树状结构,时间长了,容易忘记自己测试过哪些包,以至于有些包只是临时安装的后来忘了卸载,随着积累容易导致在系统里留下大量不必要的包。
pacman 的功能之一是可以查询安装原因,安装原因有两种“单独指定安装”和“作为其他软件包的依赖关系安装”,也可以通过 pacman -Qdt 找出所有没必要的依赖包,pacman -Qe 可以列出所有自己显式指定安装过的包,还可以通过 pacman -Rscn 卸载某个软件来将其不必要的依赖也同时卸载。
虽然 pacman 功能强大,但依然没法满足以上需求,因为很多包都是自己指定安装,但是后来忘了自己只是临时测试这个包,测试过应当卸载,然而这类包显然留在系统中,这些包还不容易被找到,要是数量庞大,基本只能重装系统才能完全干净。
强迫症患者们当然希望自己的系统是干干净净,只有自己需要的包,没有其它任何垃圾包。
于是有了新的脚本需求…
需求设计
需要这样一个脚本,这个脚本能实现:
- 能够从一个文本文档里面读取软件列表(不包含“作为其他软件包的依赖关系安装”的包,只需要指定顶层包),根据此列表与系统已安装的包进行对比,进行依赖计算,列出所有多余的包,列出所有指定了却未安装的包。
- 软件列表可以有以井号开头的注释,可以忽略空行,每行包含一个软件包。这样可以方便测试注释。
- 设计命令行参数,在必要时可以指定不同的配置文件,也可以指定自己喜欢的包管理器命令如 yay ,让脚本自动调用包管理器来同步软件列表和系统。
能满足的需求
要是真的实现了这样一个脚本,用处非常大,我在此罗列几点,充分发挥想象力的话,能带来无尽的乐趣。
-
**能根据自己的需要完全掌控系统的包:**把自己所有需要的软件做成列表(不需要考虑底层依赖),而且能对每一行的包名后面用井号注释一些自己想写的,让自己一目了然,也让系统里面出了这些包及其依赖的包之外不存在其它任何包,轻而易举地掌控系统的所有包,实现随心所欲高度定制。
-
**大幅度方便增删测试:**想测试一批软件时,只需要编辑这个软件列表,在里面添加若干行想要测试的软件,然后应用到系统,然后开始随便玩,等当不想用这些软件了,就注释或删掉那些行,再应用到系统,这些软件无影无踪,依赖也一个不留。
比如我想测试 deepin 的桌面环境,先执行 pacman -Sqg deepin 看看有哪些包,直接把这些包名复制到列表里,然后应用到系统…测完了从列表里删去,再应用到系统,完美回到没测试之前的样子。
-
**当成系统还原点:**只要我改变列表不变,那么我可以随意安装测试任何软件包,比如装 deepin 组,装任意多的包哪怕装了几十G碎碎的包,玩够了之后,直接应用列表,刚刚装的几十G直接在一分钟之内无影无踪一个不漏,完全回到测试以前的样子,此方法就是将一个列表看作是一个还原点,甚至可以设置多个还原点(多个列表)进行任意测试,配合 pacman 的装卸极速特性,基本可以随便玩了。
项目实现
实现思路
选择用 python来实现,因为 python的列表和字典非常好用,先从系统中读取所有软件包的信息,放到一个巨大的列表里,然后将每个软件包名作为字典的键,构建出一个大字典,然后对依赖进行整合,然后同样对列表里所有包进行这样处理,算出一个所需的包的集合,将系统里所有包也弄成一个集合,将两个集合直接相减,也就算出了所有多余的包了。
以下难点一个一个被攻破:
-
难点: 读取所有软件包信息,转换成 python 列表。
解决: 模拟执行 LANG=C pacman -Qi ,然后字符串处理。
-
难点: 某些包的依赖(Depends On)是一些包的提供字段(Provides)。
**解决:**利用字典的索引特性,把每个 Depends On 的内容转换成具体包名。
-
难点: 某些包的依赖(Depends On)是版本号的对比,比如 java-runtime>=8,而版本由好几个段组成,比较算法可能过于复杂。
**解决:**查询 libalpm 的开发文档得知,里面有个 C 库函数 alpm_pkg_vercmp 被封装在 libalpm.so 中,直接模拟调用,版本比较问题解决。
本项目已经用 python3 实现,我将它取名为超级包管理器,脚本名称为 spacman ,放在 github 上开源托管。方便以后直接调用,已经自己打包上传到了 aur,可以用 yay 直接安装。
1 | yay -S spacman |
所有强迫症患者的福音!
用法
命令语法
1 | 用法: spacman [-h] [--config 列表文件] [--pacman 包管理器] [--apply] [--query] |
以上用法可能已经过时没有更新,详见 spacman --help
用法举例
1 | 将 ~/.config/spacman/default.conf 列表与系统已安装的包进行对比,输出结果 |
如何写配置
到底该如何写配置文件呢,首先要自己总结出自己所有需要的包列表,写到一个文本文档里,只需要写你需要的包,不需要操心任何依赖,一行一个,格式如下:
1 | linux |
当然为了你的方便,你可以把配置文件当成笔记,顺便记录linux软件包名和功能,井号开头注释即可,也可以在包名后面跟井号注释,空行随意
1 | 内核 |
如果嫌麻烦,可以用 pacman -Qe 快速生成一个列表,这命令表示列出所有自己用pacman主动安装的包名。
1 | pacman -Qe > a.conf |
但是不建议这样做,因为生成的列表是按字母排序,而且自己整理注释起来麻烦,还不如自己从头写个。
我的日常列表也托管到 github 了,可以随时参考 spacman.conf
使用逻辑
我暂时想出以下使用方法,大家可以尽情的发挥想象发挥更多的潜力。
- 当成个人做的笔记记录,用linux用久了自己也记不清自己需要哪些包,配置文件可以刚好帮你记录。
- 配置列表中,自己想删去哪个包了,可以井号注释掉,而不必删掉一行,然后 spacman -a 即可,以后想反悔直接去掉井号注释,再次 spacman -a
- 可以把所有自己可能需要的同类软件包都记录下来,然后都用井号注释掉,然后想用哪个直接去掉哪个的井号。
- 实验各个软件,而不加进列表,只把满意的软件加进列表,不满意的由于没加进列表,直接 spacman -a 会删掉所有没进列表的软件包括其依赖,而不必一个一个 pacman -Rsc 卸载,省心又高效。例如实验各个桌面环境,一个gnome桌面环境有多少个顶层包咱们也知道。