内容纲要

🗂 | 查看 Linux 专题可浏览更多内容


如果你是从 macOS 或 Windows 而来的用户,应该很熟悉从应用商店安装软件,或互联网上获取软件的安装包,然后通过安装向导安装的方式。

在 Linux 上,安装软件的方式也多种多样。

打包系统

打包系统(packaging system)的出现简化并推进了软件管理工作。软件包中含有运行软件所需要的全部文件,包括预编译好的二进制文件、依赖信息、可由管理员定制的配置文件模板等。其中最重要的可能要算打包系统力图使安装过程尽可能的原子化(atomic),如果安装发生了错误,可以撤销(backed out)或是重新安装软件包,当有新版本时只用更新软件包即可,非常简便。

打包系统通常由两种工具组成:

  • 处理包(package)文件的安装和删除等任务的低级(Low-Level )工具。
  • 执行元数据搜索和解决依赖性问题的高级(High-Level)工具。

两者组成了一种双层(dual-layer)的全能配置管理工具。下层的工具负责安装、卸载、查询软件包,上层对应系统负责从 Internet 上查找并下载软件包、分析软件包之间的依赖关系、更新系统中所有的软件包。

低级工具

你可能已经见过 .deb.rpm 后缀的 Linux 软件安装包了,它们就像 macOS 上的 .dmg 以及 Windows 上的 .exe 安装包。

.deb 由「dpkg(Debian Package Management System)」 进行管理,常见于 Debian 及其谱系如 Ubuntu、Linux Mint 这样的 Linux 发行版上使用,而 .rpm 由「RPM(RPM Package Manager ,最早称为 Red Hat Package Manager)」进行管理,常见于 Red Hat、OpenSUSE、Fedora 等 Linux 发行版使用。

dpkg

# 使用 -i 安装指定软件
$ sudo dpkg -i <软件包名>

# 使用 -l 查询安装的某个软件包
$ sudo dpkg -l <软件包名>
# 或是
$ sudo dpkg -l | grep <软件包名>

# 使用 -r 卸载指定软件
$ sudo dpkg -r <软件包名>

# 使用 -P 卸载软件及删除其配置文件
$ sudo dpkg -P <软件包名>

此处以 VirtualBox 虚拟机为例,演示如何使用 dpkg 管理 .deb 软件包:

首先下载 VirtualBox 的 .deb 安装包,你可以在 VirtualBox 的官网找到它:https://www.virtualbox.org/wiki/Downloads

你可以使用浏览器或 wget 下载安装包:

$ wget https://download.virtualbox.org/virtualbox/6.1.4/virtualbox-6.1_6.1.4-136177~Ubuntu~bionic_amd64.deb

virtualbox-6.1_6.1.4-136177~Ubuntu~bionic_amd64.deb 文件名拆解来看:

  • virtualbox 软件名
  • 6.1_6.1.4-136177 软件版本
  • Ubuntu~bionic Linux 发行版
  • amd64 系统架构
  • deb 软件包管理格式

软件包名也是有所需要注意的地方,如上需要最注意后两位也就是 amd64debamd64 适用于 x86 架构的 64 位系统,也就是不能安装在其他架构比如 ARM 上,而 deb 如 Debian 和 Redhat 是分别使用 deb 和 rpm 的软件包格式,默认是不包含另一套包管理工具的,也就是说不能把 deb 软件包拿到默认情况下使用 rpm 包管理的发行版上使用,反之亦然。

在确定了软件包获取正确后开始安装软件:

# 使用 dpkg 命令,-i 选项表示 install 安装,-i 等同 --install
$ sudo dpkg -i virtualbox-6.1_6.1.4-136177~Ubuntu~bionic_amd64.deb

⚠️ 注意:但有时候你可能会遇上依赖问题,如下类似的报错:

dpkg: 依赖关系问题使得 virtualbox-6.1 的配置工作不能继续:
 virtualbox-6.1 依赖于 libcurl4 (>= 7.16.2);然而:
  未安装软件包 libcurl4。
 virtualbox-6.1 依赖于 libqt5core5a (>= 5.9.0~beta);然而:
  未安装软件包 libqt5core5a。
 virtualbox-6.1 依赖于 libqt5gui5 (>= 5.4.0);然而:
  未安装软件包 libqt5gui5。
 virtualbox-6.1 依赖于 libqt5opengl5 (>= 5.0.2);然而:
  未安装软件包 libqt5opengl5。
 virtualbox-6.1 依赖于 libqt5printsupport5 (>= 5.0.2);然而:
  未安装软件包 libqt5printsupport5。
 virtualbox-6.1 依赖于 libqt5widgets5 (>= 5.7.0);然而:
  未安装软件包 libqt5widgets5。
 virtualbox-6.1 依赖于 libqt5x11extras5 (>= 5.6.0);然而:
  未安装软件包 libqt5x11extras5。
 virtualbox-6.1 依赖于 libsdl1.2debian (>= 1.2.11);然而:
  未安装软件包 libsdl1.2debian。
dpkg: 处理软件包 virtualbox-6.1 (--install)时出错:
 依赖关系问题 - 仍未被配置
正在处理用于 systemd (237-3ubuntu10.39) 的触发器 ...
正在处理用于 ureadahead (0.100.0-21) 的触发器 ...
正在处理用于 gnome-menus (3.13.3-11ubuntu1.1) 的触发器 ...
正在处理用于 desktop-file-utils (0.23-1ubuntu3.18.04.2) 的触发器 ...
正在处理用于 mime-support (3.60ubuntu1) 的触发器 ...
正在处理用于 hicolor-icon-theme (0.17-2) 的触发器 ...
正在处理用于 shared-mime-info (1.9-2) 的触发器 ...
在处理时有错误发生:
 virtualbox-6.1

这个时候可以先解决依赖问题(安装日志上所提的未安装软件包)后再次安装,对于补齐依赖还可以使用 APT 自动安装所需要的软件包:

# 补齐依赖
$ sudo apt install -f

可以通过 -l 参数查询是否安装了某个软件

# -l 表示 list
$ sudo dpkg -l virtualbox
# 或是
$ sudo dpkg -l | grep virtualbox

可以通过 -r 参数删除指定软件

# -r 表示 remove
$ sudo dpkg -r virtualbox-6.1
# -P 表示 pure 删除软件及其配置文件
$ sudo dpkg -P virtualbox-6.1

⚠️ 注意:在输入到 dpkg -r virtualbox 时可能不知道具体版本号或更具体的包名,这时可以使用 Tab 键列出可选项来补全结果。

在遇到需要处理安装或卸载多个软件包时,使用空格将每个软件包名隔开即可使用一条命令处理多个软件包。

RPM

RPM 的使用方法也类似:

使用 -i (install)安装指定软件

$ sudo rpm -i <软件包名>

使用 -q (query)查询指定软件

$ sudo rpm -q <软件包名>

# 列出系统中以安装的所有软件包
$ sudo rpm -qa <软件包名>

使用 -e (erase)卸载指定软件

$ sudo rpm -e <软件包名>

使用 -U (upgrade)更新指定软件,比如找到软件包的新版本,可以使用 rpm -U 来更新替换旧版本。

$ sudo rpm -U <软件包名>

但可能并不能一帆风顺的成功更新,因为新版本可能需要新的依赖,此时尽管你可以使用强制参数选项 --force,但这并不是推荐的做法,可以先查询所依赖的那些软件包的更新版本:

$ sudo rpm -q --whatrequires <软件包名>

高级工具

APT

高级软件包工具 (Advanced Package Tool,简称 APT),是 Debian 及其衍生产品的主要命令行软件包管理器。它提供了用于搜索、管理和查询软件包信息的命令行工具,以及对 libapt-pkg 和 libapt-inst 库所提供的所有功能的低级访问,而这些功能是更高级别的软件包管理器可以依赖的。

如果使用 APT 管理来自标准仓库镜像中的安装,那么查看可用安装包最简单的方法是,如使用的是 Debian 访问 Debian — Packages 或使用的是 Ubuntu 则访问 Ubuntu Packages Search,网站提供了美观友好的界面,选择你正在使用的发行版代号和想要查找软件包就能列出结果。

软件仓库(源服务器)配置

在使用 APT 前不得不提到 APT 最重要的配置文件:/etc/apt/sources.list,以 Debian 10 官方镜像为例它的内容如下:

deb http://deb.debian.org/debian/ buster main
deb-src http://deb.debian.org/debian/ buster main

deb http://security.debian.org/debian-security buster/updates main
deb-src http://security.debian.org/debian-security buster/updates main

# buster-updates, previously known as 'volatile'
deb http://deb.debian.org/debian/ buster-updates main
deb-src http://deb.debian.org/debian/ buster-updates main

# This system was installed using small removable media
# (e.g. netinst, live or single CD). The matching "deb cdrom"
# entries were disabled at the end of the installation process.
# For information about how to configure apt package sources,
# see the sources.list(5) manual.

该配置文件告诉了 APT 应该从哪里获取软件包,具体构成如下:

  • type 字段:即软件包类型,debdeb-src 表示 .deb 类型的软件包、rpmrpm-src 表示 .rpm 类型的软件包;
  • uri 字段:指向可以从中获取到软件包的文件、HTTP 或 FTP 服务器的 URL;
  • distribution 字段:即发行版代号,如上述内容中的 buster 是 Debian 10 的代号,每个大版本都会有自己的代号;
  • components 字段:一个可能的组建清单(发行版本内的软件包分类)

举例说明:

deb http://deb.debian.org/debian/ buster main

<type> <uri> <distribution> <components>

distribution 字段和 components 字段帮助 APT 遍历软件仓库中文件系统的层次结构,这个结构的布局是有标准可循的。distribution 字段是根发行版给每个发行起的 working title,如 Debian 10 的 buster,Ubuntu 20.04 的 focal。components 字段的可用组件通常叫做 main、universe、multiverse、restricted。

在 Ubuntu 中除了基本的开源软件的 universe 组件还会有 multiverse 组件以使用一些非开源内容,如 VMware 等,也就是说如果想使用一些未受支持、存在受限许可的软件包可以加入 universe 和 multiverse 仓库。而 Debian 根据 《Debian自由软件指导方针》,所有包含在 Debian 正式发行版中的软件包,都是自由软件。

另外关于 uri 默认的配置也需要注意,默认的服务器配置可能并不适合你所在的区域,如压根就连接不上或速度极其缓慢,好在全球各地都有开源镜像站(甚至一些云服务商有自己本地的镜像站),这时候就近选择一台开源镜像服务器就可以加快速度提升使用体验。

例如 Debian 的 Debian worldwide mirror sites 和 Ubuntu 的 Official Archive Mirrors for Ubuntu 官方公布了位于全球各地的全球镜像站,可以放心的使用上面列出的镜像站或使用你自己信任的镜像站。

此处以使用「网易开源镜像站」为例通过命令快速修改源配置,特别是如果你还不熟悉 vim 等编辑器的使用,这是个比较友好的方法:

可以先使用 less /etc/apt/sources.list 查看配置中的域名是什么,如前所述我所安装的 Debian 10 中默认的地址为 deb.debian.orgsecurity.debian.org,此处我将将其修改为 mirrors.163.com

# 将 sources.list 中的 deb.debian.org 替换为 mirrors.163.com
$ sudo sed -i s/deb.debian.org/mirrors.163.com/g /etc/apt/sources.list

# 将 sources.list 中的 security.debian.org 替换为 mirrors.163.com
$ sudo sed -i s/security.debian.org/mirrors.163.com/g /etc/apt/sources.list

# 查看源文件以确认修改成功
$ less /etc/apt/sources.list

# 更新软件源
$ sudo apt update

⚠️ 这里需要提到的是,如 Debian 的 security.debian.org 和 Ubuntu 的 security.ubuntu.com 这样的地址是用于安全更新的,如 Linux Mint 等发行版有提供图形化的源服务器修改工具,在修改时上述用于安全服务的地址是不会被修改到的,包括一些 Linux 相关的专业书籍提到,一定要确保官方的安全服务器作为源加入,以便能够访问最新的安全补丁。

即便如此个人依然使用开源镜像站,因为默认地址在我这实在太慢了

APT 使用

更新升级

# 更新软件源
$ sudo apt update

# 用于安装当前系统上安装的所有包的最新版本
$ sudo apt upgrade

# 与 apt upgrade 功能相似,但除了执行升级功能外,还智能地处理与新版本包的依赖关系的变化;
# 如果有必要,它会试图以牺牲不太重要的包为代价,升级最重要的包。
# 因此,dist-upgrade 命令可能会删除一些软件包。
$ sudo apt dist-upgrade

 # 更新指定的软件包
$ sudo apt upgrade <软件包名>

# 可以使用 & 符号将多条命令一起执行
# 刷新软件源并更新需要更新的软件
# -y 参数用于自动确认提示,可用于 corn 定期更新
$ sudo apt update & sudo apt dist-upgrade -y

软件查找以及软件信息查看

# 查找某个软件包
$ apt search <软件包名>

# 显示某个软件的信息
$ apt show <软件包名>

# 列出指定(含已安装)的软件包
$ apt list -a <软件包名>
# 可以使用通配符查找包含该名字的包,如和 Nginx 相关的包
$ apt list nginx*

软件安装及卸载

# 安装某个软件包
$ sudo apt install firefox

# 卸载某个软件包
$ sudo apt remove firefox
# 卸载某个软件包及其配置文件
$ sudo apt purge firefox # 或 apt --purge remove firefox

# 卸载不需要的包
$ sudo apt autoremove

DNF

DNF 是 YUM 的下一个主要版本,它是一个基于 RPM 的 Linux 发行版的软件包管理器。

它大致保持了与 YUM 的 CLI 兼容性,并为扩展和插件定义了一个严格的 API。

# 检查可以更新的软件包
$ dnf check-update

# 更新可以升级的软件包
$ sudo dnf update # 或 dnf upgrade,更新系统中所有可以更新的软件包
$ sudo dnf update firefox # 更新指定的软件包

# 查找某个软件包
$ dnf search bash
# 显示某个软件包信息
$ dnf info bash
# 列出指定已安装的软件包
$ dnf list bash

# 安装某个软件包
$ sudo dnf install nginx
# 卸载某个软件包
$ sudo dnf remove nginx  # 或 dnf erase nginx

# 卸载不需要的包
$ sudo dnf autoremove

说到 Red Hat 系的发行版就不得不提到 EPEL(Extra Packages for Enterprise Linux),这是一个由 Fedora 特别兴趣小组创建、维护以及管理针对企业版 Linux 的一个高质量附加软件的包集。EPEL 是 Fedora 的一个精选包,但只有那些不在 RHEL 或其分层产品中的包,以避免冲突。所以你不会在 EPEL 中找到已经和 RHEL 一起发布的新版本的东西。

以 RHEL 来说,大多数人都知道 Fedora 是 RHEL 主要发行版的上游版本。出于多种原因,Red Hat 为 RHEL 提供的包集比在 Fedora 中的包集要小,因为 Fedora 中的很多软件在企业环境中是不需要的,或者不属于 RHEL 的范围。

如果作为 Fedora 用户并希望在 RHEL 工作站上安装 ImageMagick 或 Chromium 就需要 EPEL。也就是说,包括但不限于 Red Hat Enterprise Linux (RHEL)、 CentOS 和 Scientific Linux (SL)、 Oracle Linux (OL) 的发行版可以添加 EPEL 仓库以使用更多的软件包。

但同时需要注意,这是一个开源社区主导的项目,它为用户提供了很多好处,但没有生产准备或技术支持的保证。所以 EPEL 项目建议 EPEL 的用户订阅 EPEL-announce 邮件列表,以了解即将发生的问题,并在更新产生影响之前有时间进行测试。

# 启用 EPEL 仓库
$ sudo dnf install epel-release

# 启用 PowerTools 存储库,因为 EPEL 包可能依赖于这些存储库中的包
$ sudo dnf config-manager --set-enabled powertools

源码构建

在 Linux 上除了使用软件包的形式,一种常见的的安装方式是使用软件的源代码编译安装。

编译就是将源代码翻译成计算机处理器原生语言编写的代码的过程。编译器会将高级语言转换成机器语言(有的编译器会先转成汇编语言,然后使用汇编器完成最后一步的机器语言转换)。

一般常见的 UNIX 和 Linux 的软件资源(源代码、构建文件、文档以及配置模板)的分发方式为采用压缩过的归档文件形式发布,通常是以 gzip 压缩的 tar 归档(.tar.gz.tgz)文件。

编译安装的流程

系统自行安装的源代码一般位于 /usr/src 目录,而面向多用户使用的源代码则通常位于 /usr/local/src 目录,先进入目录以存放源代码文件:

$ cd /usr/local/src

本文以 iPerf3 为示例如何使用源代码编译安装一个软件:

$ sudo wget https://iperf.fr/download/source/iperf-3.1.3-source.tar.gz

一般来说,源代码包文件需要遵循一定的源代码压缩标准,该标准其中一条规定是,在解压源代码的 .tar 文件时,会创建一个包含源代码树的目录,该目录的命名形式为 project-x.xx,其中包含了项目名称及版本号。

但你并不能保证所有人都遵循标准,如果所解压的项目并不遵循该标准就会将项目文件一股脑的全解压到 /usr/local/src 中,这样目录就会乱七八糟的,所以在解压前需要检查下:

$ tar tzvf iperf-3.1.3-source.tar.gz | head
drwxrwxr-x root/root         0 2016-06-07 02:18 iperf-3.1.3/
-rw-rw-r-- root/root       336 2016-06-07 02:18 iperf-3.1.3/.gitignore
-rw-rw-r-- root/root      9498 2016-06-07 02:18 iperf-3.1.3/INSTALL
-rw-rw-r-- root/root     12786 2016-06-07 02:18 iperf-3.1.3/LICENSE
-rw-rw-r-- root/root        23 2016-06-07 02:18 iperf-3.1.3/Makefile.am
-rw-rw-r-- root/root     25154 2016-06-07 02:18 iperf-3.1.3/Makefile.in
-rw-rw-r-- root/root      8764 2016-06-07 02:18 iperf-3.1.3/README.md
-rw-rw-r-- root/root     13103 2016-06-07 02:18 iperf-3.1.3/RELEASE_NOTES
-rw-rw-r-- root/root    375041 2016-06-07 02:18 iperf-3.1.3/aclocal.m4
-rwxrwxr-x root/root      2035 2016-06-07 02:18 iperf-3.1.3/bootstrap.sh

可以看到,文件都在 iperf-3.1.3 目录下,所以可以放心解压了:

$ sudo tar -xaf iperf-3.1.3-source.tar.gz

如果你一开始将源代码归档压缩文件放在其他地方,可以指定解包到 /usr/local/src/

$ sudo tar -xaf iperf-3.1.3-source.tar.gz -C /usr/local/src/

然后进入 iperf-3.1.3 目录:

$ cd iperf-3.1.3

现在开始构建程序,首先使用 configure 程序分析构建环境(buildenvironment),这是源代码树提供的一个 Shell 脚本,多数源代码设计成可移植,在多种类 Unix 系统上构建时,略微调整源代码就可以适应系统之间的差异,它还会检查系统是否已经安装了必要的外部工具和组件。

$ sudo ./configure
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... no
...
config.status: executing libtool commands

构建配置也时常需要微调,使用 ./configure --help 可以查看特定软件包的可用配置项,如最常用的 --prefix=<路径名> 可以指定软件的安装位置,如 ./configure --prefix=/usr/local/bin/

💡 iPerf3 默认就装在 /usr/local/bin/ 所以此处就不指定了。

如上看到输出结果没有报错,configure 会在源码目录内生成一些文件,其中最重要的一个文件就是 Makefile

Makefile 是指导 make 命令如何构建可执行程序的配置文件,由于是文本文件,如果你想要查看可以使用命令:less Makefile

在搞定配置文件后就是执行安装了:

$ sudo make install

然后看看安装情况

$ which iperf3
/usr/local/bin/iperf3

综上所述,其实大体分为三个步骤:

  1. 获取源代码包文件并解压到存放源码文件的目录 /usr/local/src/
  2. 进入解压出来的软件源码目录,使用命令 ./configure 配置及 make 编译来构建程序;
  3. 使用命令 make install 安装程序;

💡 在构建配置阶段可以使用 ./configure --help 获取帮助及配置选项。

处理遇到的问题

./configuremake 的过程中有可能出现报错,这时候就需要根据报错信息排查解决问题。

仍以 iPerf3 为例,可能遇到的问题如下:
问题 1: configure: error: no acceptable C compiler found in $PATH./configure: error: C compiler cc is not found
问题 2: make: command not found

以使用 APT 为例:

$ sudo apt install build-essential

💡 有些人会在安装 build-essential 后仍会看到 C compiler cc is not found 的提示,这是权限问题,使用 sudo ./configure 或使用拥有足够权限的帐户再次运行即可。

在编译安装成功后可以使用命令查看软件的版本以此查看是否安装成功:

# -v 等同于 --version 用于查看软件的版本号,大部分软件都有该参数
$ iperf -v
# 或
$ iperf3 -v

返回:iperf 3.1.3

能看到版本号则表示安装成功了。

问题 3: 运行 iperf3 时可能会运行报错:iperf3: error while loading shared libraries: libiperf.so.0: cannot open shared object file: No such file or directory,此时解决方法为:

# 在运行 iperf3 前先运行 ldconfig
$ sudo /sbin/ldconfig
# 或是将 /usr/local/lib 加入 LD_LIBRARY_PATH 之中
$ export LD_LIBRARY_PATH=/usr/local/lib

报错日志很重要,通过报错日志解决问题也是一个良好习惯,如果你不知道报错信息是什么意思就复制它到搜索引擎搜一下吧。

另外,在实际安装不同软件时最好查看软件的官方使用说明,比如开源在 Github 的软件都会有一份 README.md 说明文件指导使用,一些软件可能还需要一些第三方依赖库,阅读使用说明是一个良好习惯并因此可少走不少弯路。

不要优先使用编译安装

不知道是受什么教材的影响,国内以 CentOS 的用户为代表的群体(甚至一些从业人员)非常痴迷于采用编译安装的方式安装软件,甚至误导了更多的人非编译安装不可,认为编译安装更能体验专业性…

但实际上这根本体现不了什么,简直是一股歪风邪气。

通常编译安装对于开发人员来说没什么问题,但最终用户和管理员就觉得不方便了,只要软件有新版本发布,就必须在每个系统上手动编译并构建源代码,这个过程既无趣又容易出错。

而更为重要的是,在编译安装时可能需要处理复杂的依赖关系以及对系统造成污染,特别是依赖,十天半个月后还记得曾经如何解决以及都有哪些依赖关系吗?当这些依赖出现安全漏洞需要及时更新呢?如果疏于更新,那么谈何安全?