vlambda博客
学习文章列表

3. Rocky Linux 基础命令及脚本介绍

通过前两章的学习,你已经掌握了 Rocky Linux 的安装以及最佳安装实践,并且准备好了一个可用的 Rocky Linux 主机。在这一章中,我们讨论如何使用 Rocky Linux 操作系统。

本章介绍的命令和操作方法,是在实际的系统管理中经常使用的,因此需要掌握好本章的内容。

3.1 用户登录并管理多用户环境

用户可以通过 Rocky Linux 提供的 “多用户文本模式”或“图形界面模式”,来执行操作系统管理以及应用程序开发等任务。对专业的 Linux 用户来说,“多用户文本模式”功能强大,操作上也更灵活,这种模式也是默认的登录模式。生产级服务器很少开启“图形界面模式”,为了减少干扰,我们在第一章介绍 Rocky Linux 的安装时,甚至没有讨论如何安装图形界面软件包。

登录是指用户在系统中确认自己身份的过程,一般使用“用户名”和“密码”进行登录,这些信息又被称为凭证。

系统可以通过多种方式访问,本章涉及的是最初级的登录方法:在安装完成后的控制台登录界面访问系统。

在安装过程中,我们新建了一个名为 “aiops” 的用户,并为它设置了密码。我们可以通过该用户,在文本控制台登录系统:

图3-1 多用户文本模式控制台登录界面

在闪烁的光标处输入用户名 “aiops”,然后按回车键,会提示我们输入密码:

3. Rocky Linux 基础命令及脚本介绍
图3.2 输入 aiops 用户的密码

输入 “aiops” 用户的密码,然后点击回车键,进行登录。在输入密码时,屏幕上是不显示任何字符的。登录成功后的会话显示如下:

3. Rocky Linux 基础命令及脚本介绍
图3-3 登录成功后的会话

现在,我们已经使用 “aiops” 的用户凭证登录到了操作系统。不同的凭证,对操作系统具有不同的访问权限。

3.1.1 使用 root 用户

普通用户无权对系统进行修改,如创建新用户或者安装软件包。要执行这些操作,需要具有管理员权限的用户,默认是 root 用户。这个用户是系统自带的,UID (用户标识符) 为 0。

在之前的安装过程中,我们已经为 root 用户设置了密码,我们通过控制台登录 root 用户。控制台可以有多个终端会话,默认是 tty1,也就是我们通过 aiops 用户登录的终端会话。同时按下 Ctrl + Alt + F1 键可以切换到默认终端。我们可以同时按下 Ctrl + Alt + F2 键切换到第二个终端,其余终端依次类推。默认提供了 6 个终端。如果你在虚拟机环境中使用 Ctrl + Alt + F1...6 不能正常切换终端时,也可以在当前终端下输入 exit 命令,退出 aiops 用户,再使用 root 用户登录:

图3.4 使用 root 用户登录系统

3.1.2 命令提示符

登录系统后,可以看到等待我们输入并执行命令的行,它被称为“命令提示符”。在默认配置中,[] 里显示了用户名和主机名以及当前所在的目录。这些信息能直观的告诉我们当前以哪个用户在哪台主机的哪个目录下进行操作。~ 表示用户的家目录,对于 aiops 用户来说,家目录是 “/home/aiops” 目录,对于 root 用户来说,家目录是 “/root” 目录。

[] 外还有一个符号,这个符号表示了当前登录系统的是普通用户还是管理员用户:

  • $:表示当前用户是普通用户。
  • #:表示当前用户是 root 用户,或者是获得管理员权限的用户。

# 提示符的终端下执行命令时要小心谨慎,因为你是在以管理员的身份对系统进行操作,此时你可以对系统做任何事情,哪怕是执行极具破坏性的操作。

当然,在运行的会话中,我们也可以切换用户。

3.2 su 命令切换用户

从“多用户文本模式”这个名称不难想到,我们可以在用户间进行切换。这种切换可以是不同的终端,但在工作中更多遇到的是在一个终端下切换用户。

Linux 系统中的 su 命令,可用于在同一个终端下切换用户。我们以 root 用户登录了系统,现在尝试把 root 用户切换成 aiops 用户。在执行用户切换前,可以通过 whoami 命令查看当前用户:

[root@rocky08-host ~]# whoamiroot

从 root 切换到 aiops:

[root@rocky08-host ~]# su aiops[aiops@rocky08-host root]$ whoami aiops

切换后,我们进入了 aiops 用户的会话。可以通过 exit 命令退出 aiops 用户会话,重回 root 用户会话:

[aiops@rocky08-host root]$ exitexit[root@rocky08-host ~]# whoami root

从 root 用户 su 到任何其他用户时,不需要输入密码,因为 root 用户可以对操作系统做任何事情。当我们从普通用户 su 到 root 用户时,则需要输入 root 密码进行验证:

[aiops@rocky08-host ~]$ su rootPassword: [root@rocky08-host aiops]# whoami root

由于 root 是 ID 为 0 的用户,也是最重要的用户,执行 su 命令而不指定用户时,默认会切换到 root:

[aiops@rocky08-host ~]$ suPassword: [root@rocky08-host aiops]# whoami root

每个用户都可以在自己的环境中配置一些选项,如编辑器。如果我们想切换到某个用户,并使用它们的自定义选项 (环境变量) 时,可以通过在 **su ** 命令后添加 - 来实现:

[aiops@rocky08-host ~]$ su -Password: Last login: Wed Jan 19 11:35:38 CST 2022 on pts/1[root@rocky08-host ~]#

此外,我们还可以从 root 切换到 aiops:

[root@rocky08-host ~]# su - aiopsLast login: Wed Jan 19 11:39:10 CST 2022 on pts/1[aiops@rocky08-host ~]$

su - 的行为,就像完成了一次新登录,但是在同一个会话中进行的。在下一节中,我们讨论如何管理系统中用户的权限。

3.3 用户、组和基本权限

多用户环境的定义是为了能够同时处理多个用户。但为了能够管理系统资源,又需要以下两种功能:

  • Groups:能够以块的形式聚合用户并为其提供权限。每个用户都有一个主组。默认情况下,会为每个用户创建一个同名组,同名组也是用户的主组。
  • Permission:分配给文件,用来定义该文件可以被哪个用户和组访问。标准的 Linux (Unix/POSIX) 权限包括 user、group 和 other。

操作系统会为文件和目录分配一组默认权限,修改权限时要小心谨慎。

Unix 的设计哲学之一:一切皆文件。这一点被 Linux 很好的继承了。在 Linux 系统中,几乎所有东西都是一个文件。磁盘被表示为文件,进程被表示为文件,其他组件也被表示为文件......

这意味着,在为文件分配权限时,我们也可以将权限分配给由它们实现的其他组件和功能,因为在 Linux 中,所有内容都表示为文件。

3.3.1 Users

用户是为操作人员以及系统中运行的程序提供安全限制的一种方式。Linux 系统中的用户分为三种类型:

  • 普通用户:分配给个人的,有适当的权限限制。
  • 超级用户:也被称为 root,是系统的主要管理账号,对系统有完全访问权限。
  • 系统用户:这类用户一般分配给正在运行的进程或者守护进程,用来限制进程在系统中的权限。系统用户不能用来登录操作系统。

用户有一个被称为 UID (用户标识) 的数字,系统通过 UID 识别用户。

通过 whoami 命令可以查询当前登录系统的用户,但为了获取更多信息,我们可以使用 id 命令:

[aiops@rocky08-host ~]$ iduid=1000(aiops) gid=1000(aiops) groups=1000(aiops),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

我们还可以查看系统中其他用户账号的信息,甚至可以获取 root 的信息:

[aiops@rocky08-host ~]$ id rootuid=0(root) gid=0(root) groups=0(root)

id 命令返回的用户信息解释如下:

  • uid=1000(aiops):用户 ID 是用户在系统中的数字标识,在本例中,用户 ID 为 1000。在 Linux 系统中,1000 及以上的标识符用于普通用户,999 及以下的标识符用于系统用户,0 则为 root 用户。
  • gid=1000(aiops):组 ID 是分配给用户的主组的数字标识符。
  • groups=1000(aiops),10(wheel):这些是用户所在的组,在本例中,aiops 组 ID (gid) 为 1000,wheel 组 ID 为 10。wheel 用户组是一个特殊的组。在 Linux 系统中,它被用作用户组,该组中的用户可以通过 sudo 命令成为管理员。
  • context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023:aiops 用户的 SELinux 上下文。它将使用 SELinux 在系统中定义一些限制。SELinux 的内容,会在以后的章节中介绍。

与 ID 相关的数据,保存在 /etc/passwd 文件中。这个文件中的数据比较敏感,最好使用相关的工具来管理它。如果我们有编辑它的需求,可以使用 vipw 命令,vipw 可以确保不论任何时候,仅有一个管理员在编辑该文件。/etc/passwd 文件的每一行都是一个用户的信息。这是 aiops 的那行:

aiops:x:1000:1000:aiops:/home/aiops:/bin/bash

在每行中,字段间用 : 分隔。我们一起看看这些字段的含义:

  • aiops:分配给用户的用户名。

  • x:加密的密码字段。在本例中显示为 x,因为该字段已经移动到 /etc/shadow 文件中保存了,为使系统更加安全,普通用户无权访问该文件。

  • 1000:第一个 1000 为 UID 的值。

  • 1000:第二个 1000 为 GID 的值。

  • aiops:账号的描述信息。

  • /home/aiops:用户的主目录,也是用户的默认工作目录。

  • /bin/bash:用户的命令解释器。Bash 是 Rocky Linux 中默认的解释器。还有一些其他解释器如 zsh、fish 等,都可以安装在 Rocky 中。

3.3.2 Groups

组以动态的方式将权限分配给用户集合。我们可以想象一个场景,有一个开发团队。我们可以创建 develop 组,并为该组提供 /apps/logs/ 目录的读取权限。当开发团队有新同学加入时,为了向新人提供该目录的访问权限,我们只需将其加入 develop 组中。如果有人离开,我们也只需将其账号从 develop 组中删除。

组有一个叫做 GID 的数字,操作系统通过 GID 识别组。

组信息存储在系统的 /etc/group 文件中。为了确保一致性,避免损坏该文件,我们可以使用 vigr 工具编辑该文件。文件中每一行表示一个组,不同字段间以 : 分隔。我们看看 wheel 组所在行:

wheel:x:10:aiops

各字段含义如下:

  • wheel:组名称。wheel 是一个比较特殊的组,因为在默认情况下,它是向普通用户提供管理特权的组。
  • x:该字段是组的密码字段。已经过时了,之所以还保留,是为了兼容性。
  • 10:该组的 GID。
  • aiops:该组中的用户列表。用户以 , 分隔,例如:aiops, weiwendi

组有以下三种类型:

  • Primary group:主组,用户创建的文件会被分配给该组。
  • Private group:私有组,与用户的名字相同,创建新用户时,同时也会自动创建该组。一般 Primary 与 Private 组是同一个。
  • Supplementary group:附加组,通常为特定目的创建的组。如 wheel 组,我们可以将普通用户加入该组,为其提供系统管理权限;cdrom 组用于提供对系统中的 CD 和 DVD 设备的访问。

3.3.3 文件权限

可以使用 ls 命令列出文件,查看文件的权限。我们使用 root 用户登录操作系统,然后执行 ls 命令:

[root@rocky08-host ~]# lsanaconda-ks.cfg

~ 表示目前我们正处于 root 用户家目录。执行 ls 命令后,我们看到当前目录下有一个 Kickstart 文件。

可以通过在 ls 后添加 -l 选项,获取文件的详细信息:

[root@rocky08-host ~]# ls -ltotal 4-rw-------. 1 root root 1296 Jan 10 15:07 anaconda-ks.cfg

输出内容释义:

  • total 4:文件在磁盘中占用的总空间,以千字节 (KB) 为单位。我们使用的是 4K 块,所以即使文件小于 4K,也会占用至少 4K。
  • -rw-------.:表示分配给文件的权限。
图3-5 Linux 权限结构

第一个字符表示文件可能具有特殊权限。如果它是一个常规文件,并且没有特殊权限,就会显示为 -

  • d:目录会显示为 d。在 Linux 中,所有内容都是文件,目录是具有特殊权限的文件。
  • l:链接,通常是符号链接,会显示为 l。它们的行为类似于从不同目录访问文件的快捷方式。类似我们在 Windows 系统上,把某个软件图标发送到桌面,打开软件时,只需执行桌面的快捷图标。
  • s:以不同用户或组的身份运行文件的特殊权限称为 setuid 或 setgid,将显示为 s
  • t:一个特殊的权限,只能由所有者删除或重命名文件,称为 sticky bit,将显示为 t

接下来的三个字符 rw-,是所有者的权限:

  • 第一个是 r,用来分配可读权限。
  • 第二个是 w,用来分配可写权限。
  • 第三个是 x,用来分配可执行权限。在示例中,该位置为 -,说明所有者不具备执行权限。对于目录来说,可执行权限代表能够进入这个目录中。

后面三个 --- 字符表示组权限,其工作方式与所有者权限相同。在这种情况下,不授予组任何权限。

最后三个 --- 字符表示其他用户也没有访问该文件的任何权限。其他用户指即非所有者用户,也非所有组中的用户。

  • 1:表示该文件的硬链接数量。这样做的目的之一是,删除该文件时,不会删除在另一个文件夹中使用的硬链接文件。
  • root:第一个 root,表示文件所有者。
  • root:第二个 root,表示文件所有组。
  • 1296:表示文件大小,以字节为单位。
  • Jan 10 15:07:文件最后被修改的日期和时间。
  • anaconda-ks.cfg:文件名。

当我们列出目录 (文件夹) 时,输出的是目录中内容。可以添加 -d 选项,列出目录本身的信息。我们以 /etc 目录为例 (/etc 目录中存储了操作系统相关的配置文件):

[root@rocky08-host ~]# ls -d -l /etcdrwxr-xr-x. 84 root root 8192 Jan 20 11:33 /etc

通过命令行获取文件和目录的相关信息还是非常简单的。下一节我们会讨论更多关于命令行和如何查看文件系统的知识,以便在系统中切换目录。

3.4 命令行

正如我们前面看到的,登录到系统后,就可以访问命令行了。命令行常见的用途是执行命令和导航文件系统。

3.4.1 命令行和环境变量

命令行是由程序提供的,这个程序被称为解释器或 shell。它的行为取决于我们使用的 shell,但在本节中,我们将讨论 Linux 中使用最广泛的 shell,以及 Rocky 默认提供的 shell:bash。

可以通过以下命令查看正在使用的 shell:

[root@rocky08-host ~]# echo $SHELL/bin/bash

echo 命令将在屏幕上显示我们给它的任何内容。有些内容需要替换或解释,比如环境变量。要替换的内容以 $ 符号开头。在本例中,我们告诉系统回显 SHELL 变量的内容。通过 echo 显示其他变量上:

[root@rocky08-host ~]# echo $USERroot[root@rocky08-host ~]# echo $HOME/root

这些是为每个用户定制的环境变量,我们来查看下 aiops 用户:

[root@rocky08-host ~]# su - aiopsLast login: Thu Jan 20 11:42:48 CST 2022 on pts/0[aiops@rocky08-host ~]$ echo $USERaiops[aiops@rocky08-host ~]$ echo $HOME/home/aiops

HOME 也是可用的,它的值是当前用户的家目录。

常见的环境变量有:

变量名 变量值 用途
HOSTNAME rocky08-host.aiops.red 系统的主机名,如果有 DNS 记录,可以通过该名称访问主机
LANG en_US.UTF-8 当前用户的语言配置。当前示例中,使用的美式英语的 UTF-8 扩展
PATH /home/aiops/.local/bin:/home/aiops/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin : 分隔的目录列表,会在这些目录检索运行的命令
PS1 [\u@\h \W]$ 定义了提示符中将显示的信息,在当前示例中,用户 (\u),@ 主机名 (\h),当前目录 (\W),普通用户($),root 用户(#)
PWD /home/aiops 当前的工作目录
SHELL /bin/bash 使用的 shell
USER aiops 用户名,当前示例中该名称是 aiops
HOME /home/aiops 当前用户的家目录

可以在 ~/.bashrc 文件中修改当前用户的变量值。

3.4.2 文件系统导航

现在是时候进入系统目录树了。在 Linux 和 Unix 中 (macOS 是类 Unix 系统),没有驱动器号 (Windows 中的 C 盘、D 盘),只有一个单一的目录树,以根目录 (用  / 表示) 开始。系统的其他内容都挂载在根目录上,包括磁盘和其他设备,它们会被分配一个可访问的目录。

根目录和 root 用户的主目录是两回事。root 用户默认分配的主目录是 /root,而根目录是所有目录的父目录,用 / 表示。

可以通过 pwd 命令查看当前所在的目录:

[aiops@rocky08-host ~]$ pwd/home/aiops

想要切换到其他目录,可以使用 cd 命令:

[aiops@rocky08-host ~]$ cd /var/tmp/[aiops@rocky08-host tmp]$ pwd/var/tmp

我们之前介绍过,~ 是用户主目录的快捷方式,我们可以使用这个快捷方式回到用户主目录:

[aiops@rocky08-host tmp]$ cd ~[aiops@rocky08-host ~]$ pwd/home/aiops

目录的几个快捷方式:

  • ~:当前用户的主目录
  • .:当前目录
  • ..:父目录
  • -:上次使用的目录

3.4.3 Bash 自动补全

快捷方式是一种快速到达常用目录或相对引用当前工作目录的方法。但 bash 也提供了快速访问其他目录的功能,就是自动补全功能。这个功能需要使用 Tab 键完成。

要进入某个目录时,我们可以使用 Tab 键来补全该目录的名称。例如,我们想切换到 “/boot/grub2” 目录中,可以输入以下内容:

[aiops@rocky08-host ~]$ cd /bo

然后按 Tab 键,它会自动完成 /boot/ 的输入,甚至会加上最后的 /,因为它是一个目录:

[aiops@rocky08-host ~]$ cd /boot/

现在我们输入要进入的 “grub2” 目录的第一个字母,也就是 g:

[aiops@rocky08-host ~]$ cd /boot/g

然后再按下 Tab 键,它会完成 “/boot/grub2” 目录的补全:

[aiops@rocky08-host ~]$ cd /boot/grub2/

现在我们可以点击回车键,进入该目录。当然,普通用户是没有权限进入这个目录的。我们需要切换到 root 用户才能进入该目录。

假如我们连续按两次 Tab 键,会显示一个可用的目标列表:

[root@rocky08-host ~]# cd /rroot/ run/

它也可以用来补全命令。我们可以输入一个字母,例如 “h”,再按两次 Tab 键,会列出所有以 “h” 开头的命令:

[root@rocky08-host ~]# hhalt hardlink hash hdparm head help hexdump history hostid hostname hostnamectl hwclock

Bash 可以帮助使用者快速补全命令、变量名、目录名,提高命令输入效率。我们也可以安装 “bash-completion” 包,对补全功能进行扩展,以完成补全命令的其他部分:

[root@rocky08-host ~]# dnf install -y bash-completion

3.4.4. 文件系统结构

Linux 有一个标准,由 Linux 基金会维护,定义了文件系统的层次结构,几乎所有 Linux 发行版都遵循此标准,包括 Rocky Linux。这个标准被称为 FHS (Filesystem Hierarchy Standard)。我们一起来查看下标准及系统中重要的目录:

  • /:根目录。文件系统层次结构的主目录,其他目录挂载在根目录上。
  • /boot:存储了系统引导时使用的文件,包括 Linux 内核及启动镜像。该目录会写入磁盘最头部的分区,通常划分为独立分区。
  • /dev:devices 的缩写,存放 Linux 的外部设备 (如磁盘、键盘、音频设备)。在 Linux 中访问设备的方式和访问文件的方式相同。
  • /etc:editable text configuration,可编辑文本配置。 /etc/ 目录中存放着应用于系统范围的配置文件。
  • /home:存储普通用户的主目录,其中包括用户的个人配置及文件,比如 aiops 用户的数据就在 /home/aiops/ 目录中。
  • /media:挂载外部媒体的目录,如 U盘、光驱等,被系统识别后,会挂载到该目录下,供系统访问。
  • /mnt:用于临时挂载的文件系统。
  • /opt:操作系统提供的,用于安装自定义软件的目录。
  • /proc:Processes。一个特殊的虚拟文件系统,表示系统中运行的进程。
  • /root:root 用户主目录。该目录并不在 /home/ 下,而是独立的 /root/ 目录,目的是避免 /home/ 目录空间被其它用户占满,导致 root 用户无法登录系统。
  • /run:存放了进程的运行时数据,当系统重启时,该目录下的文件会被清理,当停止某个应用时,对应目录中的数据会被删除。
  • /srv:该目录用来存放对外提供的内容,如 web 页面。不过这些并不是绝对的,比如很多团队会自行创建一个目录,来存储服务数据。
  • /sys:存储内核特性和连接设备相关的系统信息。
  • /usr:存储只读的用户数据 (如库、二进制文件、头文件、源文件和其他共享数据)。
  • /usr/bin:存储了在系统中常用的常规二进制文件。 /bin/ 目录是此目录的符号链接。
  • /usr/lib:存储了在系统中常用的库文件。 /lib/ 目录是此目录的符号链接。
  • /usr/local:存放本地二进制文件。当我们编译安装某程序时,默认会安装在该目录中的对应目录下。
  • /usr/sbin:该目录存储了仅供超级用户使用的二进制文件。 /sbin/ 目录是此目录的符号链接。
  • /var:存储了不同程序管理的内容,如日志文件。

要熟悉操作系统中的各个目录,这样才能更好的使用操作系统。

3.5 I/O 重定向

我们已经执行了一些命令,并能从中获取系统的相关信息,例如使用 ls 命令列出文件,并从命令的输出中或得了一些信息,如文件名、文件大小。这些输出的信息可能是有用的,我们希望能够正确使用、存储和管理它们。

当讨论命令的输入和输出时,有三个源或目标需要我们了解:

  • STDOUT:也称“标准输出”,命令会将常规消息通过“标准输出”输出,表达它们正在做什么。在交互式的 shell 终端中,“标准输出”会输出到屏幕上。这将是我们管理的主要输出。
  • STDERR:也称为“标准错误”,命令输出错误信息的地方。在交互式的 shell 终端中,“标准错误”也将与“标准输出”一样,被输出到屏幕上,除非我们对其进行重定向。
  • STDIN:也被称为“标准输入”,获取数据的地方。

命令的输入与输出的使用方式需要以下操作符:

  • |:管道 (pipe) 操作符用于获取前一个命令的输出,并使其作为下一个命令的输入。它将数据从一个命令传递给另一个命令。
  • >:重定向 (redirect) 操作符用于将命令的输出存储到文件中。如果文件存在,将覆盖文件中的内容。
  • <:反向重定向 (reverse redirect) 操作符可以将文件作为命令的输入,使用它不会删除该文件。
  • >>:重定向和添加 (redirect and add) 操作符可以将命令的输出追加到文件中。如果文件不存在,将创建该文件。
  • 2>:重定向标准错误 (redirect STDERR) 操作符只重定向发送错误消息处理器的输出。 2> 之间没有空格。
  • 1>:重定向标准输出 (redirect STDOUT) 操作符只重定向发送到标准输出,而不是发送到错误信息的处理器。
  • >&2:重定向到 STDERR 操作符将把输出重定向到标准错误处理程序。
  • >&1:重定向到 STDOUT 操作符将输出重定向到标准输出处理程序。

为了更好地理解这些概念,我们接下来看一些示例。

获取一个文件列表,并将列表内容存储到文件中。我以 /var 目录为示例。使用 ls 命令列出 /var 目录中的文件 (和目录) 列表,可以使用 -m 参数,将每个条目以逗号隔开:

[root@rocky08-host ~]# ls -m /varadm, cache, crash, db, empty, ftp, games, gopher, kerberos, lib, local, lock, log, mail, nis, opt, preserve, run, spool, tmp, yp

再次执行 ls -m /var 命令,将输出重定向到 /root/var-files.txt 文件中:

[root@rocky08-host ~]# ls -m /var > /root/var-files.txt [root@rocky08-host ~]#

这次,屏幕上没有显示输出,但我们可以在 /root 目录中找到新创建的文件:

[root@rocky08-host ~]# lsanaconda-ks.cfg var-files.txt

我们可以使用 cat 命令,将文件内容显示到屏幕上:

[root@rocky08-host ~]# cat var-files.txtadm, cache, crash, db, empty, ftp, games, gopher, kerberos, lib, local, lock,log, mail, nis, opt, preserve, run, spool, tmp, yp

我们也可以把 /var/lib 目录中的内容添加到文件中。首先查看下 /var/lib 目录中有哪些内容:

[root@rocky08-host ~]# ls -m /var/lib/alternatives, authselect, chrony, containerd, dbus, dhclient, dnf, games, initramfs, kdump, logrotate, misc, NetworkManager, nginx, os-prober, plymouth, polkit-1, portables, private, rpm, rpm-state,rsyslog, selinux, sss, systemd, tpm, tuned, unbound, vmware

现在我们使用 >> 操作符把输出的内容追加到 var-files.txt 文件中:

[root@rocky08-host ~]# ls -m /var/lib/ >> var-files.txt [root@rocky08-host ~]# cat var-files.txtadm, cache, crash, db, empty, ftp, games, gopher, kerberos, lib, local, lock,log, mail, nis, opt, preserve, run, spool, tmp, ypalternatives, authselect, chrony, containerd, dbus, dhclient, dnf, games,initramfs, kdump, logrotate, misc, NetworkManager, nginx, os-prober, plymouth,polkit-1, portables, private, rpm, rpm-state, rsyslog, selinux, sss, systemd,tpm, tuned, unbound, vmware

var-files.txt 文件中包含了 /var/var/lib 的以逗号分割的列表。

我们尝试列出一个不存在的目录,以查看打印出的错误:

[root@rocky08-host ~]# ls -m /nonls: cannot access '/non': No such file or directory

我们看到的输出是一个错误,系统对它的处理与常规消息不同。我们可以尝试将输出重定向到一个文件:

[root@rocky08-host ~]# ls -m /non > non-listing.txtls: cannot access '/non': No such file or directory[root@rocky08-host ~]# cat non-listing.txt [root@rocky08-host ~]#

对执行出错的命令使用标准重定向,错误消息依然会打印到屏幕上,并创建一个空文件。这是因为该文件包含通过 STDOUT 显示的消息。如果要对 STDERR 重定向,可以使用 2>

[root@rocky08-host ~]# ls /non 2> error.txt[root@rocky08-host ~]# cat error.txt ls: cannot access '/non': No such file or directory

现在,我们可以分别重定向标准输出和错误输出了。

现在我们看看管道的用法。假设我们需要统计 /var 目录下文件、目录的数量,我们可以使用 wc 命令,它用于统计,-w 选项只统计词数。为了做到这一点,我们需要使用 |,将 ls 的输出作为 wc 的输入:

[root@rocky08-host ~]# ls -m /var/ | wc -w21

我们也可以用它统计 /etc/ 目录中的条目:

[root@rocky08-host ~]# ls -m /etc/ | wc -w184

管道可以重复使用命令的输出,把输出传递给另一个命令进行处理。Linux 中还有一些常用的处理管道输出的命令,我们下一节讨论它们。

3.6 使用 grep、sed 过滤输出

在系统管理中,经常使用 grep 命令。它以行级别对文件中的内容或者标准输入的内容进行模式匹配。

我们使用 find 命令把 /usr 目录下的文件进行递归搜索,并把结果重定向到 /root/user-files.txt 文件中:

[root@rocky08-host ~]# find /usr/ > /root/usr-files.txt[root@rocky08-host ~]# ls -lh usr-files.txt -rw-r--r--. 1 root root 2.1M Jan 28 17:27 usr-files.txt

生成的文件有 2.1M,从这么大的文本中查找内容并不容易。系统中有一个压缩命令叫 gzip,我想知道 /usr/ 中哪些文件包含了 gzip 模式。为了过滤出包含 gzip 的文件,我们可以使用以下命令:

[root@rocky08-host ~]# grep gzip usr-files.txt/usr/bin/gzip/usr/lib64/python3.6/__pycache__/gzip.cpython-36.opt-2.pyc/usr/lib64/python3.6/__pycache__/gzip.cpython-36.opt-1.pyc/usr/lib64/python3.6/__pycache__/gzip.cpython-36.pyc/usr/lib64/python3.6/gzip.py/usr/share/licenses/gzip/usr/share/licenses/gzip/COPYING/usr/share/licenses/gzip/fdl-1.3.txt/usr/share/doc/gzip/usr/share/doc/gzip/AUTHORS/usr/share/doc/gzip/ChangeLog/usr/share/doc/gzip/NEWS/usr/share/doc/gzip/README/usr/share/doc/gzip/THANKS/usr/share/doc/gzip/TODO/usr/share/man/man1/gzip.1.gz/usr/share/bash-completion/completions/gzip/usr/share/info/gzip.info.gz/usr/share/mime/application/gzip.xml/usr/share/vim/vim80/autoload/gzip.vim/usr/share/vim/vim80/doc/pi_gzip.txt/usr/share/vim/vim80/plugin/gzip.vim

除了对文件进行搜索,我们也可以在不创建文件的情况下,做同样的事情。通过管道,我们可以把 find 的输出重定向给 grep,得到的结果是相同的:

[root@rocky08-host ~]# find /usr/ | grep gzip/usr/bin/gzip/usr/lib64/python3.6/__pycache__/gzip.cpython-36.opt-2.pyc/usr/lib64/python3.6/__pycache__/gzip.cpython-36.opt-1.pyc/usr/lib64/python3.6/__pycache__/gzip.cpython-36.pyc/usr/lib64/python3.6/gzip.py/usr/share/licenses/gzip/usr/share/licenses/gzip/COPYING/usr/share/licenses/gzip/fdl-1.3.txt/usr/share/doc/gzip/usr/share/doc/gzip/AUTHORS/usr/share/doc/gzip/ChangeLog/usr/share/doc/gzip/NEWS/usr/share/doc/gzip/README/usr/share/doc/gzip/THANKS/usr/share/doc/gzip/TODO/usr/share/man/man1/gzip.1.gz/usr/share/bash-completion/completions/gzip/usr/share/info/gzip.info.gz/usr/share/mime/application/gzip.xml/usr/share/vim/vim80/autoload/gzip.vim/usr/share/vim/vim80/doc/pi_gzip.txt/usr/share/vim/vim80/plugin/gzip.vim

在这个命令中,find 的标准输出通过管道传递给了 grep 处理。我们还可以使用 wc 统计文件的数量,不过这次需要使用 -l 选项,来统计行数:

[root@rocky08-host ~]# find /usr/ | grep gzip | wc -l22

现在,我们连接了两个管道,一个用于过滤输出,一个用于统计输出。当我们在系统中搜索和查找信息时,管道是最常用且高效的手段。

以下是 grep 的常用选项:

  • -i:--ignore-case,匹配模式忽略大小写。
  • -v:--invert-match,显示与搜索的模式不匹配的所有条目。
  • -r:--recursive,对目录进行递归匹配。

有时我们还会经常对输出的列进行过滤。假设我们想查看 /root 目录中文件的大小,可以使用以下命令:

[root@rocky08-host ~]# ls -ltotal 2140-rw-------. 1 root root 1296 Jan 10 15:07 anaconda-ks.cfg-rw-r--r--. 1 root root 52 Jan 28 16:45 error.txt-rw-r--r--. 1 root root 0 Jan 28 16:37 non-listing.txt-rw-r--r--. 1 root root 2178227 Jan 28 17:27 usr-files.txt-rw-r--r--. 1 root root 388 Jan 28 16:28 var-files.txt

假设我们只想获取名称中包含 “files” 的文件的大小,也就是第五列,可以使用 awk 命令:

[root@rocky08-host ~]# ls -l | grep files | awk '{ print $5}'2178227388

awk 工具将帮助我们根据正确的列进行过滤。它对于查找进程中的标识符或从长输出中获取特定的数据列表非常实用。

awk 的默认分隔符是空格,我们可以通过 -F 参数指定分隔符,以下是将 : 作为分隔符的示例:

[root@rocky08-host ~]# awk -F: '{ print $1}' /etc/passwdrootbindaemonadmlpsyncshutdownhaltmailoperatorgamesftpnobodydbussystemd-coredumpsystemd-resolvetsspolkitdunboundsssdchronysshdaiopsnginx

awk 和 grep 是 Linux 系统管理员非常常用的处理工具,要想管理好系统的输出,就必须要很好的理解它们。我在这里提到的仅是基础知识,它们还有很多强大的功能有待你去发现。在了解了对输出按行及列过滤的操作后,我们接下来讨论如何管理系统中的文件。

3.7 处理文件和目录

在命令行管理文件和目录,也是系统管理中的常见操作。它是我们管理和复制重要数据 (如配置文件或数据文件) 的基础。

3.7.1 目录

使用 mkdir (make directory 的缩写) 命令,可以创建目录:

[aiops@rocky08-host ~]$ mkdir mydir[aiops@rocky08-host ~]$ ls -ltotal 0drwxrwxr-x. 2 aiops aiops 6 Feb 3 17:25 mydir

rmdir (remove directory 的缩写) 命令用来删除目录:

[aiops@rocky08-host ~]$ mkdir deletedir[aiops@rocky08-host ~]$ ls -ltotal 0drwxrwxr-x. 2 aiops aiops 6 Feb 3 17:28 deletedirdrwxrwxr-x. 2 aiops aiops 6 Feb 3 17:25 mydir[aiops@rocky08-host ~]$ rmdir deletedir[aiops@rocky08-host ~]$ ls -ltotal 0drwxrwxr-x. 2 aiops aiops 6 Feb 3 17:25 mydir

rmdir 命令仅能删除空目录:

[aiops@rocky08-host ~]$ echo 'hello world.' > mydir/hello_world.txt[aiops@rocky08-host ~]$ rmdir mydirrmdir: failed to remove 'mydir': Directory not empty

我们可以使用 rm (remove 的缩写) 命令删除目录及目录中包含的子目录和文件。我们先创建 “test.txt” 文件,并删除它:

[aiops@rocky08-host ~]$ echo 'delete' > test.txt[aiops@rocky08-host ~]$ ls -l test.txt-rw-rw-r--. 1 aiops aiops 11 Feb 3 17:37 test.txt[aiops@rocky08-host ~]$ rm test.txt [aiops@rocky08-host ~]$ ls -l test.txtls: cannot access 'test.txt': No such file or directory

要删除完整的目录分支,可以为 rm 命令加上递归参数 -r

[aiops@rocky08-host ~]$ rm -r mydir[aiops@rocky08-host ~]$ ls -ltotal 0

要谨慎使用递归模式进行删除操作,因为没有针对它的恢复命令,也没有垃圾箱保存命令行中以删除的文件。

以上三个命令的总结表格如下:

命令 作用
mkdir 创建目录
rmdir 删除空目录
rm -r 递归删除目录及目录中包含的子目录和/或文件

现在我们已经知道了如何在 Linux 系统中创建和删除目录,接下来我们对目录及其内容进行复制和移动。

3.7.2 复制和移动

cp 命令用来复制文件和目录,我们以拷贝 awk 的示例作为演示对象:

[aiops@rocky08-host ~]$ mkdir myawk[aiops@rocky08-host ~]$ cp /usr/share/awk/* myawk/[aiops@rocky08-host ~]$ ls myawk/ |wc -l26

为了同时复制多个文件,我们使用了 * 通配符。在拷贝所有文件时,我们可以使用 * 通配符代替。我们可以先输入初始字符,然后再输入 *,接下来我们试着使用通配符复制文件:

[aiops@rocky08-host ~]$ mkdir mysystemd[aiops@rocky08-host ~]$ cp /usr/share/doc/systemd/* mysystemd/[aiops@rocky08-host ~]$ cd mysystemd/[aiops@rocky08-host mysystemd]$ ls20-yama-ptrace.conf CODING_STYLE DISTRO_PORTING ENVIRONMENT.md GVARIANT-SERIALIZATION HACKING NEWS README TRANSIENT-SETTINGS.md TRANSLATORS UIDS-GIDS.md

运行 ls TR* 命令时,只会显示 “TR” 开头的文件:

[aiops@rocky08-host mysystemd]$ ls TR*TRANSIENT-SETTINGS.md TRANSLATORS

它还可以以同样的方式处理以某个字符结尾的文件:

[aiops@rocky08-host mysystemd]$ ls *.mdENVIRONMENT.md TRANSIENT-SETTINGS.md UIDS-GIDS.md

如你所见,该命令只显示了以 “.md” 结尾的文件。

cp 命令也有递归选项 -r,用来复制目录的完整分支:

[aiops@rocky08-host ~]$ mkdir myauthselect[aiops@rocky08-host ~]$ cp -r /usr/share/authselect/* myauthselect[aiops@rocky08-host ~]$ ls myauthselect/default vendor

mv 命令用来移动目录或文件,我们把刚创建的目录移动到一个名为 docs 的目录中:

[aiops@rocky08-host ~]$ mkdir docs[aiops@rocky08-host ~]$ mv my* docs/[aiops@rocky08-host ~]$ ls docs/myauthselect myawk mysystemd

使用 mv 命令,不需要递归选项就可以管理一个完整的文件和目录分支。mv 命令也通常用来重命名文件或目录:

[aiops@rocky08-host ~]$ cd docs/mysystemd/[aiops@rocky08-host mysystemd]$ ls -l NEWS -rw-r--r--. 1 aiops aiops 451192 Feb 3 18:02 NEWS[aiops@rocky08-host mysystemd]$ mv NEWS mynews[aiops@rocky08-host mysystemd]$ ls -l NEWSls: cannot access 'NEWS': No such file or directory[aiops@rocky08-host mysystemd]$ ls -l mynews -rw-r--r--. 1 aiops aiops 451192 Feb 3 18:02 mynews

touch 命令可以用来创建空文件:

[aiops@rocky08-host ~]$ ls docs/myauthselect myawk mysystemd[aiops@rocky08-host ~]$ touch docs/touchfile[aiops@rocky08-host ~]$ ls -l docs/total 4drwxrwxr-x. 4 aiops aiops 35 Feb 3 18:08 myauthselectdrwxrwxr-x. 2 aiops aiops 4096 Feb 3 17:51 myawkdrwxrwxr-x. 2 aiops aiops 238 Feb 3 18:15 mysystemd-rw-rw-r--. 1 aiops aiops 0 Feb 3 18:18 touchfile

对已经存在的文件或目录使用 touch 命令时,会将其访问文件更新为执行 touch 时的时间:

[aiops@rocky08-host ~]$ touch docs/touchfile [aiops@rocky08-host ~]$ ls -l docs/total 4drwxrwxr-x. 4 aiops aiops 35 Feb 3 18:08 myauthselectdrwxrwxr-x. 2 aiops aiops 4096 Feb 3 17:51 myawkdrwxrwxr-x. 2 aiops aiops 238 Feb 3 18:15 mysystemd-rw-rw-r--. 1 aiops aiops 0 Feb 3 18:20 touchfile

以上三个命令的总结表格如下:

命令 作用
cp 复制源目录中的文件
cp -r 递归复制源目录的完整目录分支
touch 创建空文件,或者设置现有文件的访问时间为当前时间
mv 对文件或目录进行重命名
mv 移动完整的目录分支到目标目录

3.7.3 符号链接和硬链接

我们可以使用链接将相同的文件放在两个地方。有两种类型的链接:

  • Hard Links:硬链接。在文件系统中会有两个记录 (或更多) 指向同一个文件。其内容仅被写入磁盘一次。同一个文件的硬链接不能跨文件系统创建。不能为目录创建硬链接。
  • Symbolic Links:符号链接或软链接。可以为系统中任何位置的文件或目录创建符号链接。

两者都是使用 ln 命令创建。

我们首先演示硬链接的创建:

[aiops@rocky08-host ~]$ cd docs/[aiops@rocky08-host docs]$ ln mysystemd/README MYREADME1[aiops@rocky08-host docs]$ ls -ltotal 20drwxrwxr-x. 4 aiops aiops 35 Feb 3 18:08 myauthselectdrwxrwxr-x. 2 aiops aiops 4096 Feb 3 17:51 myawk-rw-r--r--. 2 aiops aiops 13826 Feb 3 18:02 MYREADME1drwxrwxr-x. 2 aiops aiops 238 Feb 3 18:15 mysystemd-rw-rw-r--. 1 aiops aiops 0 Feb 3 18:20 touchfile[aiops@rocky08-host docs]$ ln mysystemd/README MYREADME2[aiops@rocky08-host docs]$ ls -ltotal 36drwxrwxr-x. 4 aiops aiops 35 Feb 3 18:08 myauthselectdrwxrwxr-x. 2 aiops aiops 4096 Feb 3 17:51 myawk-rw-r--r--. 3 aiops aiops 13826 Feb 3 18:02 MYREADME1-rw-r--r--. 3 aiops aiops 13826 Feb 3 18:02 MYREADME2drwxrwxr-x. 2 aiops aiops 238 Feb 3 18:15 mysystemd-rw-rw-r--. 1 aiops aiops 0 Feb 3 18:20 touchfile

可以看到,文件的引用数量随着硬链接文件的增加而增加。

符号链接使用 ln -s 命令创建:

[aiops@rocky08-host docs]$ ln -s mysystemd mysystemdlink[aiops@rocky08-host docs]$ ls -ltotal 36drwxrwxr-x. 4 aiops aiops 35 Feb 3 18:08 myauthselectdrwxrwxr-x. 2 aiops aiops 4096 Feb 3 17:51 myawk-rw-r--r--. 3 aiops aiops 13826 Feb 3 18:02 MYREADME1-rw-r--r--. 3 aiops aiops 13826 Feb 3 18:02 MYREADME2drwxrwxr-x. 2 aiops aiops 238 Feb 3 18:15 mysystemdlrwxrwxrwx. 1 aiops aiops 9 Feb 3 18:56 mysystemdlink -> mysystemd-rw-rw-r--. 1 aiops aiops 0 Feb 3 18:20 touchfile

“l” 表示是符号链接,“d” 表示目录。

在工作中,符号链接更为常见。

命令总结:

命令 作用
ln 在同一个文件系统中对文件创建硬链接
ln -s 对文件或目录创建符号链接,可以是在不同文件系统中。

正如你所看到的,创建硬链接和符号链接非常简单。通过链接,我们可以从不同位置访问同一个文件或目录。在下一节中,我们讨论如何打包和压缩一组文件和目录。

3.7.4 归档和压缩

有时,我们想把一个完整的目录,包括文件,打包成一个文件,以达到备份的目的,或者仅仅是为了更容易地分发它。使用 tar 命令可以将文件打包。

我们可以尝试以 root 用户的身份创建 /etc 目录分支的备份:

[root@rocky08-host ~]# tar -cf etc-backup.tar /etc/tar: Removing leading `/' from member names[root@rocky08-host ~]# ls -lh etc-backup.tar -rw-r--r--. 1 root root 24M Feb 3 20:57 etc-backup.tar

选项说明:

  • -c:create (创建) 的简写。 tar 可以把文件归档,也可以解开已归档文件。
  • -f:file (文件) 的简写。我们指定下一个参数将处理一个文件。

可以使用以下命令解包:

[root@rocky08-host ~]# mkdir tmp[root@rocky08-host ~]# cd tmp/[root@rocky08-host tmp]# tar -xf ../etc-backup.tar [root@rocky08-host tmp]# lsetc

选项说明:

  • -x:提取,把 TAR 文件解包。

介绍了文件归档,我们再来看看文件的压缩。压缩文件或目录一般使用 gzip 命令。我们复制 /etc/services 文件并压缩它:

[root@rocky08-host tmp]# cp /etc/services .[root@rocky08-host tmp]# ls -lh services -rw-r--r--. 1 root root 677K Feb 3 21:09 services[root@rocky08-host tmp]# gzip services [root@rocky08-host tmp]# ls -lh services.gz -rw-r--r--. 1 root root 140K Feb 3 21:09 services.gz

使用 gzip 压缩时,压缩后的文件以 .gz 作为扩展名,原始文件不会被保留。另外,gzip 压缩后的文件,是原文件的五分之一大小。

解压缩文件可以使用 gunzip 命令:

[root@rocky08-host tmp]# gunzip services.gz [root@rocky08-host tmp]# ls -lh services -rw-r--r--. 1 root root 677K Feb 3 21:09 services

我们会把归档的文件进行压缩:

[root@rocky08-host ~]# tar cf etc-backup.tar /etc/tar: Removing leading `/' from member names[root@rocky08-host ~]# ls -lh etc-backup.tar -rw-r--r--. 1 root root 24M Feb 3 21:18 etc-backup.tar[root@rocky08-host ~]# gzip etc-backup.tar [root@rocky08-host ~]# ls -lh etc-backup.tar.gz -rw-r--r--. 1 root root 5.4M Feb 3 21:18 etc-backup.tar.gz

这样,我们通过两步对文件完成了归档和压缩。

归档和压缩通常一起使用,所以 tar 命令提供了一些选项,可以在一个步骤中完成打包和压缩:

[root@rocky08-host ~]# rm -f etc-backup.tar.gz [root@rocky08-host ~]# tar czf etc-backup.tar.gz /etc/tar: Removing leading `/' from member names[root@rocky08-host ~]# ls -lh etc-backup.tar.gz -rw-r--r--. 1 root root 5.4M Feb 3 21:22 etc-backup.tar.gz

选项介绍:

  • -z:使用 gzip 压缩新创建的 tar 文件。它也可用于解压。

解压缩时提供同样的选项:

[root@rocky08-host ~]# cd tmp/[root@rocky08-host tmp]# rm -rf etc[root@rocky08-host tmp]# tar zxf ../etc-backup.tar.gz [root@rocky08-host tmp]# lsetc

使用 tar 和 gzip 打包和压缩文件非常简单。还有其他更高比率的压缩方法,如 bzip2、xz,你可以尝试一下这些命令。接下来我们把学到的命令,通过 shell 脚本,组合成一种强大的自动化方式。

3.7.5 Shell 脚本

有时,我们可能需要多次在操作系统上执行一系列命令,虽然每次都可以逐条手动执行这些命令,但有更高效的方法,就是通过 shell 脚本。

Shell 脚本是一个文本文件,其内容包括 shell 的命令解释器及要执行的命令列表。

我们编辑名为 “hello.sh” 的文件来创建我们的第一个 shell 脚本,它的内容如下:

echo "hello world!"

然后我们可以通过 bash 命令解释器运行 “hello.sh” 脚本:

[root@rocky08-host ~]# bash hello.shhello world!

还有另一种写法,在执行时可以省略 bash 命令。我们在脚本的首行添加引用解释器,完整的内容如下:

#!/bin/bashecho "hello world!"

为 “hello.sh” 文件添加可执行的权限:

[root@rocky08-host ~]# chmod +x hello.sh

接着我们就可以执行该文件了:

[root@rocky08-host ~]# ./hello.shhello world!

这就是我们创建的第一个 Shell 脚本。

如果命令的路径包含在 $PATH 变量中,那么我们可以在任何位置执行该命令。如果命令 (或 shell 脚本) 不在 $PATH 包含的目录中,我们就需要指定运行目录,示例中使用的是 . 快捷方式表示当前路径,以及 / 分隔符。

脚本中也可以使用变量。除了系统变量,我们还可以提供变量名和变量值来自定义变量。vi使用一个变量代替 “world” 这个词。要使用变量,需要在变量名前加上 $ 符号。脚本内容如下:

#!/bin/bashPLACE="world"echo "hello $PLACE!"

运行脚本,可以得到与之前相同的结果:

[root@rocky08-host ~]# ./hello.shhello world!

为了更清晰,在使用变量的值时,我们将它的名称放在花括号 {"VariableName"} 中,并将此作为一个最佳实践。优化后的脚本内容如下:

#!/bin/bashPLACE="world"echo "hello ${PLACE}!"

我们演示了如何创建基础的脚本,实现起来非常的简单。在工作中,我们经常会遇到通过程序化的功能,进行深入控制的需求,这就需要用到循环。接下来我们讨论循环。

3.7.6 for 循环

如果我们计划在某个列表上执行同一个命令,该怎么办呢?可以使用 for 循环,它可以迭代一组元素,比如说一个列表或一个计数器。

for 循环的语法如下:

  • for:指定迭代
  • do:指定动作
  • done:关闭循环

我们定义一个以空格分隔的列表,并通过 for 循环遍历该列表:

#!/bin/bashPLACES_LIST="XuChang BeiJing China World"for PLACE in ${PLACES_LIST}; doecho "hello ${PLACE}!"done

运行脚本,输出的内容如下:

[root@rhel8 ~]# ./hello.shhello XuChang!hello Beijing!hello China!hello World!

在工作中,经常使用 for 循环通过外部命令读取列表,外部命令放在 ()。

我们用 **ls **命令,演示 for 通过外部命令获取列表。首先创建名为 “listdirs.sh” 的脚本,内容如下:

#!/bin/bashfor DIRS in $(ls -l |grep ^d |awk {'print $NF'}); do echo "Directory: ${DIRS}"done

awk {'print $NF'} 表示打印管道传递的内容的最后一列。

我们讨论了用 for 循环迭代列表的几种方法,当设计到自动化任务时,这是比较常用的。接下来,我们来讨论脚本中的另一个程序化功能:条件判断。

3.7.7 if 条件判断

有时,我们需要对列表的元素执行不同的操作,或者针对某个条件执行特定的命令。if 条件语句可以帮我们实现这一点。

if 条件语句的语法:

  • if:指定条件。条件通常包含在中括号里 [ condition ]

  • then:指定动作。

  • fi:关闭 if 语句。

我们在之前的 hello.sh 中加入 if 语句,用英文向世界问好:

#!/bin/bashPLACES_LIST="XuChang BeiJing China World"for PLACE in ${PLACES_LIST}; do if [ ${PLACE} = "World" ]; then echo "hello world!" fidone

运行 hello.sh 脚本,输出内容如下:

[root@rocky08-host ~]# ./hello.shhello world!

我们向 World 问好,但对于不符合条件的 XuChang、BeiJing、China,我们可以使用 else 语句,语法如下:

  • else:当条件不匹配时,被用作 then 元素。

我们再次修改 hello.sh 脚本,加入 else 条件句:

#!/bin/bashPLACES_LIST="XuChang BeiJing China World"for PLACE in ${PLACES_LIST}; do if [ ${PLACE} = "World" ]; then echo "hello world!" else echo "你好世界!" fidone

执行结果如下:

[root@rocky08-host ~]# ./hello.sh你好世界!你好世界!你好世界!hello world!

通过以上示例可知,在脚本中使用条件判断语句的用法简单,并能对命令运行条件进行很多控制。现在我们需要控制在什么时候某些东西可能无法正常运行。这就是 exit 码 (或 error codes) 的用处。

3.7.8 exit codes

当一个程序运行时,会提供一个 exit code,表示程序运行是否正常。exit code 存储在 $? 变量中。

我们查看下 ls hello.sh 执行后的 exit code:

[root@rocky08-host ~]# ls hello.shhello.sh[root@rocky08-host ~]# echo $?0

当程序正常运行时,exit code 为 0

当我们列出不存在的文件 (或错误的运行命令、有问题的文件) 时,我们再来看看 exit code 的值。

[root@rocky08-host ~]# ls noneexistentfile.txtls: cannot access 'nonexistentfile.txt': No such file or directory[root@rocky08-host ~]# echo $?2

这时,exit code 为非 0。我们可以查阅文档,了解对应数字的问题性质。

在脚本中运行命令时,要检查 exit code 并做相应的处理。下一节,我们讨论在哪里可以获取命令的帮助信息,如 exit code 或其他选项。

3.8 使用帮助

Linux 系统中包含几百个命令,很少有人能记住所有命令,所以,为提高用户的系统管理技能,系统中提供了帮助资源。man 命令是获取命令语法和用法的最重要的帮助资源。除此之外,我们还可以使用 “command --help” 显示对应命令的简单的选项列表。

系统中有很多帮助信息,可以提高使用者的系统管理技能。包括手册页 (man pages)、信息页 (info pages)、其他文档以及命令的 help 选项。

3.8.1 help 选项

命令都会提供 --help 选项,以帮助用户快速了解该命令的用法。在使用 --help 选项时,几乎所有命令都会显示摘要信息。--help 显示的选项列表主要是命令选项的概述及如何快速使用命令,不包含命令的更详细信息。

3.8.2 Man

几乎所有命令都提供了手册页,你可以通过手册页,查看命令的详细功能及用法。通过 man 命令,可以在命令行查阅手册页。我们以 man tar 为例:

[root@rocky08-host ~]# man tar

我们可以看到 tar 命令的相关信息,按 q 键可以退出手册页。

手册页中有相关主题的部分。通过 apropos 命令可以搜素这些主题。我们同样以 tar 命令为例:

[root@rocky08-host ~]# apropos tar

从输出看,它不仅匹配了 ”tar“,还匹配了 ”start“。这虽然不完美,但它可以提供与 ”tar“ 相关的有用信息,比如 gpgtar。

手册页有不同的部分。正如我们在示例中看到的,tar 在手册页中有两个部分,一个是命令行工具 (第 1 部分),一个是归档格式 (第 5 部分)。

tar (1) - an archiving utilitytar (5) - format of tape archive files

我们可以通过以下命令查看第 5  部分的页面,以了解归档格式:

[root@rocky08-host ~]# man 5 tar

我们可以看到 tar 格式页面:

TAR(5) BSD File Formats Manual TAR(5)
NAME tar — format of tape archive files
DESCRIPTION The tar archive format collects any number of files, directories, and other file system objects (symbolic links, device nodes, etc.) into a single stream of bytes. The format was originally designed to be used with tape drives that operate with fixed-size blocks, but is widely used as a gen‐ eral packaging mechanism.

可以看出,手册页是学习正在使用的命令的宝贵资源。在对系统的日常操作中,遇到不确定的命令,可以多翻翻手册页。

3.8.3 Info

Info pages 通常比 main pages 有更多的描述,而且交互性强,更有助于我们了解一个主题。

我们可以通过运行以下命令来获取 ls 命令的信息:

[root@rocky08-host ~]# info ls

输出内容如下:

Next: dir invocation, Up: Directory listing
10.1 ‘ls’: List directory contents==================================
The ‘ls’ program lists information about files (of any type, includingdirectories). Options and file arguments can be intermixed arbitrarily,as usual.

信息页可以重定向到其他主题,以下划线显示。将光标放在这些主题上并按 Enter 键。

与手册页一样,按 q 退出。

如果在手册或信息页中没有找到对应的主题怎么办呢?我们在下一节介绍。

3.8.4 其他文档资源

/usr/share/doc 目录中,可以找到操作系统中安装的工具所附带的其他文档。

我们来看看这个目录中有多少项:

[root@rocky08-host ~]# cd /usr/share/doc/[root@rocky08-host doc]# ls |wc -l352

可以看到,在 /usr/share/doc/ 目录中有 352 个目录,当然,这个值根据安装的软件包的数量会有变化。

我们以 bash 目录作为示例,进入 bash 目录:

[root@rocky08-host doc]# cd bash/[root@rocky08-host bash]# lsbash.html bashref.html FAQ INTRO RBASH README[root@rocky08-host bash]# less INTRO BASH - The Bourne-Again Shell
Bash is the shell, or command language interpreter, that will appearin the GNU operating system. Bash is an sh-compatible shell thatincorporates useful features from the Korn shell (ksh) and C shell(csh). It is intended to conform to the IEEE POSIX P1003.2/ISO 9945.2Shell and Tools standard. It offers functional improvements over shfor both programming and interactive use. In addition, most sh scriptscan be run by Bash without modification.

通过该文档,可以很好的了解 Bash。

3.9 小结

在本章中,我们讨论了如何使用普通用户及 root 用户登录操作系统,以及权限和安全的基本知识。我想通过这一章的学习,你应该更习惯使用带有自动补全功能的命令行,导航、压缩和解压缩、重定向命令输出并解析目录和文件,甚至使用 Shell 脚本自动化过程。更重要的是,通过包含的文档,我们可以在任何 Linux 系统中获得关于我们正在做 (或想要做) 什么的信息。这些技能是接下来章节的基础。如果你对本章的学习有些滞涩,不要犹豫,请重新阅读这一章。