最近正好在研究 dpkg
和 rpm
,对 Linux 依赖有了更深的认识。
在网上看了很多,所有 Docker、虚拟机、编译安装、以及各种另辟蹊径的答案,都是面向日常繁重的业务没时间折腾而不得已做出的妥协和让步。
而我们面向技术的,“从来都喜欢正面硬刚!”
硬刚 Linux 软件安装依赖问题的办法有很多,我给他分为两大类!
“一类合法,另一类暴力。”
先说合法的解决方案
也是所有人都知道的解决方案:
代码语言:javascript复制sudo apt-get install xxxxx
一般情况下,它会连带软件的依赖一起安装。如果这个过程中依赖安装失败,就执行:
代码语言:javascript复制sudo apt-get -f install
一次不行两次,只要源里有,只要能保证依赖关系是顺畅的,再多的依赖多执行几次都能装完。
如果有依赖源里找不到。这个坑就踩不过去了,解决办法是:找到缺失的库的安装包手动下载下来。然后通过 sudo dpkg -i xxxx.deb
安装。
需要手动下载安装包的寻找主要有两个途径:
- 百度找,直接搜包名 版本号并带上关键字 deb
- 通过源。
百度直接找库就不多说了,额外说一下通过源怎么找。
你在网上搜 ubuntu 国内源。会找到很多类似这样的写法:
代码语言:javascript复制deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial main restricted universe multiverse
这其实即是给apt-get
工具配置的源地址,也是个实际的网址,你可以直接从浏览器里访问到,比如上面这个:
https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial main restricted universe multiverse
它其实代表的是:https://mirrors.tuna.tsinghua.edu.cn/ubuntu/pool/xenial/
这个路径下的 main
、restricted
、universe
、multiverse
这几个目录:
apt-get
工具会在这四个的目录下自动检索对应的软件包下载并安装。
在一些特殊情况,比如机器网络受限,但浏览器开了代理可以访问外网的情况下。apt-get
无法从源里获取软件,你可以从这里手动找到对应的软件包下载下来然后使用 dpkg
安装。
比如这里,手动找到 mysql
的安装包路径,再点击就可以直接下载:
刚才说道 apt-get install
无法修复依赖,通过手动下载然后把这些缺失的安装包装上之后,就可以通过 apt-get
把刚才装不上的包装上。
这是非常合理合法的解决方案。
再补充一种合法技巧,可以尝试用:
代码语言:javascript复制apt-get install 本地软件包
这是因为:
“依赖检测”和“软件安装”不是 apt-get
做的,而是 dpkg
做的。依赖不满足“自动修复依赖”才是 apt-get
做的。
所以,如果你下载了一个 deb 安装包通过 dpkg
安装,但依赖不满足的话,他只会提示你依赖缺失,但他不会自动寻找并安装依赖,虽然你仍然可以去下载安装缺失的依赖,但他如果缺失十个八个的,你再手动下载然后 dpkg
安装也不现实了。
举个例子:我这里下载了一个搜狗输入法的安装包,dpkg -i
无法安装,但是可以通过 apt-get install
装上:
要注意:通过 apt-get
安装本地软件一定要写路径,相对绝对都可以,但不能只写包名。不然它会去源里面找不会装本地的。
上面的方案几乎可以解决 80% 的安装依赖问题。总结一下:
- 安装软件就用:
sudo apt-get install xxxx
- 遇到依赖问题:
sudo apt-get -f install
- 如果有缺失无法安装,就去网上下,缺什么下什么,下载下来后
sudo apt-get install ./xxxx
把缺的包安装上,再装原来的包。
接下来说说暴力的解决方案
之所以说暴力,是因为刚才的方式,已经是在我的认知里,工具和系统提供的自动化程序能做到的极限。
如果还是出现了依赖无法满足的问题,比如什么即将安装 xxxx 但是现在的系统内的版本高于 xxxx。
一般这种情况,系统已经不建议你再搞了,你如果非要硬来,很可能会破坏现有的程序依赖结构。
- 运气好或者你手高,硬装上一点问题没有。
- 影响小一点,你装了这个软件,另一个软件不能用或者会崩。
- 影响大一点,有些系统命令不好使了,界面卡死了,资源管理器崩了等等。
- 影响更大一点,装完用着啥毛病没有,重启后卡在加载过程,再也进不了系统。
“以上这些情况,我全部都遇到过!”
所以下面的搞法,你就当学习知识或看我装逼都行,自己实操,还是要谨慎地折腾。至少,别在客户机器和生产环境的服务器上硬来。
暴力的解决方案有:
--ignore-depends
,忽略依赖直接装。- 解压安装包,删掉依赖字段重新打包。
- 修改系统中记录的
status
文件。 - 无视安装失败,直接运行。
- 直接拿到根目录,就地解压。
- 改掉
dpkg
源码,直接不检测依赖。一个一个说:
1、--ignore-depends
这是所有暴力方案里技术门槛最低的一个,你可以通过 dpkg --help
查看 --ignore-depends
选项:
选项: ......其它选项 --ignore-depends=<软件包>,... 忽略关于 <软件包> 的所有依赖关系。 --force-... 忽视遇到的问题(参见 --force-help)。 ......其它选项
这个选项可以指定要忽略的依赖包。所以安装的时候如果依赖不满足,你直接加上这个参数把依赖忽略的就完了:
当然,安装不会有任何问题,但是能不能用就看要造化了。
“并不是依赖不满足,装上就一定完全不能用”,有时候只是功能不全而已。
比如你装了一个 QQ,它依赖 ffmpeg,你忽略了它后直接安装使用很可能没问题,但是一点击视频通话,程序立马就崩掉了。(这是个假设,QQ 用啥不用啥我也不知道)
这个方案虽然门槛低,有个致命的缺陷就是太麻烦,我这里缺两个,写两个 ignore 参数。安装过程中经常遇到那种一下子缺十个八个的,要写十个八个这样的参数属实费劲。
2、解压安装包,删掉依赖字段重新打包
这个方案可以直接大刀阔斧地把软件包的依赖全干掉,不过稍微需要点技术底子:
“首先你得会解压安装包,其次你得会制作安装包。”
dpkg -X
只会解压出安装包的文件,无法解压出安装包带的脚本和控制信息。
“右键”->“提取到此处”,解压出来的control
和data
分开两个压缩包,也不是打包前完美的状态。
这里要用的是
代码语言:javascript复制dpkg-deb -R sogoupinyin_xxxx.deb ./sogou
这样解压出来的就是 deb 在被打包前完完整整的样子,我解压了一个搜狗输入法的安装包为例:
3、修改系统中记录的 status 文件
和刚才那个方案思路差不多,只不过换了一个突破点,比刚才要更方便一点。
刚才我们突破的思路是:安装包里记录有对软件依赖,全部干掉就没有依赖了。
那么这个方案的思路则是:如果检测依赖发现系统不满足,我们给他伪造一个满足的依赖环境不就完了!
刚才我们说的 control
文件,所有的 deb 文件都有,安装过程中(不是安装后也不是前),它就被记录在了系统里的:/var/lib/dpkg/status
文件里。
这个文件里的内容,也是 dpkg -l
命令显示内容的信息来源。
也是我们上面说的,依赖检测时检索系统内是否满足依赖的信息来源。
如果软件通过
dpkg -r
卸载,这个status
文件里的信息不会删除,只会把Status
字段改为:deinstall ok config-files
。通过dpkg -P
或者dpkg --purge
才会把信息完全抹除。
所以,依赖不满足的时候,你可以直接打开这个文件,仿照其他软件的写法,照抄一段加上,把文件名改为缺失的依赖包的名字就可以!dpkg 就会认为,系统里有安装这个包,从而解决依赖导致的无法安装的问题。
同样,如果出现依赖的包需要的版本不满足的情况,你也可以直接找到文件里对应的包的信息,改掉 Version
字段到一个满足需求的版本就可以。
当然,实际上系统里是没有安装这些库的,我们只是骗过了 dpkg
。
系统里的软件信息一般都写得特别多,这里简单提供个样例,实在不会抄系统的就抄这个:
代码语言:javascript复制Package: mtest
Priority: optional
Section: editors
Maintainer: Threedog Team <qiugeyafang@gmail.com>
Architecture: all
Version: 1.0.0
Homepage: http://www.threedog.top
Description: test
只要把 Package
换成缺失的依赖包的名字,加到 status
文件里就可以了。记得和其他软件信息之间要有空行。
4、无视安装失败,直接运行。
这个方案之所以可行,是 dpkg
对软件安装过程的执行机制决定的。dpkg
对依赖的处理时机是:“先释放文件,再检测依赖,然后再完成最终配置。”
其实
control
文件里还可以写一个字段叫做预依赖:Pre-Depends
。这个字段的检测级别和时机与Architecture
字段相当。写在这个字段里面的依赖,如果检测不满足,安装会直接中断,不会释放文件,status
中不会记录,系统中也不会留下任何痕迹。
所以,当 dpkg 爆出依赖不满足的问题的时候,其实包里的文件已经释放到系统里了,只不过没有做后续的配置。比如:桌面图标配置、字体配置、文件关联设置、启动触发设置等等。
但这并不妨碍你直接找到他的可执行程序文件直接执行。
如果你不知道他往系统里释放了哪些文件:第一,可以解压看下目录结构;第二,可以通过 dpkg --contents xxxx.deb
查看包里包含哪些文件。
也可以用 dpkg -S 软件名
查看已经安装的软件在系统里装了哪些东西。
然后找到二进制可执行文件,一般都会往 /usr/bin/
下面放一个,运气好的话直接执行有可能能跑起来。
5、暴力解压
这个方案属实有点过于暴力并且不合理。直接把安装包移动到根目录下,然后直接 dpkg -X
解压到当前。然后像刚才一样找二进制可执行程序调用。
前提是你知道这软件包里有什么并且明确它不会影响什么的话。
不然如果解压出的文件破坏了系统的重要文件,那直接就是不可逆的毁降维打击。
而且这么搞完,如何卸载也是一个问题......
6、改 dpkg 源码
这是技术上最硬核的解决方案。
主要操作方法如下:
从这个地址: https://git.dpkg.org/git/dpkg/dpkg.git
克隆下来 dpkg
源码,在源码里的 packages.c
里面找到这个 dependencies_ok
函数。甭管它里面写了多少东西,直接在最开头给它 return DEP_CHECK_OK;
或者 return 2;
反正就是个枚举。
然后把项目编译一下生成自己的 dpkg
用就可以。