Linux-基础知识-全-

Linux 基础知识(全)

原文:zh.annas-archive.org/md5/29980B7659BC4BE41209BC2F2B7B6D02

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

在这本书中,目标是建立一个扎实的基础,学习 Linux 命令行的所有基本要素,让你入门。它的设计强调只学习实际的核心技能和基本的 Linux 知识,这在开始学习这个美妙的操作系统时非常重要。本课程中展示的所有示例都经过精心选择,是初学者或系统管理员在从零开始时可能遇到的日常和现实世界的任务、用例和问题。我们从虚拟化软件开始我们的旅程,并安装 CentOS 7 Linux 作为虚拟机。然后,我们将温柔地向您介绍最基本的命令行操作,如光标移动、命令、选项和参数、历史记录、引用和通配符、文件流和管道,以及获取帮助,然后向您介绍正则表达式的奥妙以及如何处理文件。然后,演示和解释了最基本的日常 Linux 命令,并提供了对 Bash shell 脚本的简洁介绍。最后,读者将介绍高级主题,如网络、如何排除系统故障、高级文件权限、ACL、setuid、setgid 和粘性位。这只是一个起点,关于 Linux 还有很多东西可以学习。

这本书是为谁写的

这本书是为那些希望成为 Linux 系统管理员的个人而写的。

本书涵盖了什么

《第一章》Linux 简介,向您介绍了 Linux 的一般概念。主题涵盖从虚拟化和 VirtualBox 和 CentOS 的安装,到 VirtualBox 的工作动态,以及与 VirtualBox 的 SSH 连接。

《第二章》Linux 命令行,阐明了一系列主题,包括 shell 通配符、命令行操作的介绍、在 Linux 文件系统中导航文件和文件夹的中心思想、不同流的中心思想、正则表达式以及 grep、sed 和 awk 等重要命令。

《第三章》Linux 文件系统,着重介绍了系统的工作动态,包括文件链接、用户和组、文件权限、文本文件、文本编辑器以及对 Linux 文件系统的理解。

《第四章》使用命令行,带您了解基本的 Linux 命令、信号、附加程序、进程和 Bash shell 脚本。

《第五章》更高级的命令行和概念,概述了基本的网络概念、服务、ACL、故障排除、setuid、setgid 和粘性位。

从这本书中获得最大的收益

你需要一个基本的实验室设置,至少有 8GB 的 RAM 和双核处理器的系统。如果你计划创建一个虚拟环境,那么建议使用具有相同内存和四核处理器的系统。

对于 Windows 系统,VirtualBox 和 VMware 工作站是最佳选择。对于 Mac 系统,可以在 parallels 上运行测试系统。

在整本书中,我们使用了 CentOS 7 minimal 作为操作系统。

下载彩色图片

我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图片。您可以在这里下载:www.packtpub.com/sites/default/files/downloads/FundamentalsofLinux_ColorImages.pdf

使用的约定

本书中使用了许多文本约定。

CodeInText:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。例如:“第一个 CentOS 7 VM 服务器现在可以使用 IP 127.0.0.1和端口2222访问,第二个端口2223,第三个端口2224。”

任何命令行输入或输出都以以下形式编写:

# yum update -y 

粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词会在文本中以这种方式出现。例如:“选择我们的 CentOS 7 服务器 VM,然后单击绿色的 Start 按钮启动它。”

警告或重要说明会以这种形式出现。

提示和技巧会以这种形式出现。

联系我们

我们的读者的反馈总是受欢迎的。

一般反馈:发送电子邮件至feedback@packtpub.com,并在主题中提及书名。如果您对本书的任何方面有疑问,请发送电子邮件至questions@packtpub.com

勘误:尽管我们已经尽一切努力确保内容的准确性,但错误确实会发生。如果您在本书中发现错误,我们将不胜感激,如果您能向我们报告。请访问www.packtpub.com/submit-errata,选择您的书,点击勘误提交表单链接,并输入详细信息。

盗版:如果您在互联网上发现我们作品的任何形式的非法副本,我们将不胜感激,如果您能向我们提供位置地址或网站名称。请通过copyright@packtpub.com与我们联系,并附上材料链接。

如果您有兴趣成为作者:如果您在某个专题上有专业知识,并且有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com

评论

请留下评论。阅读并使用本书后,为什么不在购买它的网站上留下评论呢?潜在读者可以看到并使用您的客观意见来做出购买决定,我们在 Packt 可以了解您对我们产品的看法,我们的作者也可以看到您对他们书籍的反馈。谢谢!

有关 Packt 的更多信息,请访问packtpub.com

第一章:Linux 简介

操作系统OS)是一种特殊的软件,运行在您的计算机上,使得启动和运行诸如 Microsoft Word 和 Excel 之类的程序成为可能。除此之外,它还为您处理计算机的输入和输出,并提供文件系统和硬件控制。您可能已经了解的操作系统的例子有 Windows、macOS、iOS 或 Android。

在本章中,我们将涵盖以下主题:

  • Linux 系统概述

  • 虚拟化

  • 安装 VirtualBox 和 CentOS

  • 使用 VirtualBox

  • 通过 SSH 连接虚拟机

Linux 系统概述

Linux 不是特定和完整工作的操作系统的名称,而是仅实现操作系统的基本内核,称为内核。大多数类型的 Linux 操作系统都是免费的,但提供成千上万的程序和软件,完全免费使用。其中大多数程序也是开源的,这意味着您可以查看程序的确切蓝图,以及任何人都可以更改它。Linux 非常流行并且被广泛使用的一个非常重要的领域是管理许多网络服务。这些是在 Linux 服务器后台运行的程序,不断等待外部事件以响应某种操作或信息。例如,流行的互联网服务如 Web 服务器,用于向用户呈现网站;用于电子邮件通信的主服务器;以及用于存储和传递任何类型数据的数据库服务器。如前所述,Linux 只是内核部分的名称,而不是完整的工作操作系统。要使其完整,您需要将其与各种程序捆绑在一起,然后称为发行版

如今,人们可以在众多不同的 Linux 发行版中进行选择,每个都设计用于特定目的,各自具有优缺点。它们之间的主要区别在于所选软件与 Linux 内核捆绑在一起。最重要的 Linux 发行版家族是基于 Red Hat 和 Debian 的 Linux 发行版。CentOS 是目前最重要的免费 Red Hat 基础 Linux 服务器发行版之一。它是一个非常稳定、安全和可靠的操作系统,因此经常用于在企业环境中运行非常关键的网络服务。此外,值得知道的是,该操作系统的开发非常强大,更新也经过精心选择和适当测试。本书的第一章是关于安装 Linux;在这里,我们将通过向您介绍虚拟化的概念来轻松开始。然后,我们将使用名为 VirtualBox 的免费虚拟化软件创建一个新的 CentOS 7 虚拟机VM)。

虚拟化

在这一部分,我们将为您概述虚拟化的概念。我们还将向您展示如何使用 VirtualBox,并在此之后向您展示如何在 VirtualBox 中安装您的第一个 CentOS 7 虚拟机。虚拟化是一个非常热门的话题,目前的核心 IT 技能就是虚拟化。

简而言之,虚拟化是一种技术,可以在同一台计算机上并行运行独立的操作系统。例如,如果您目前正在使用 Windows 计算机,您可以在桌面上并行运行另一个操作系统,如 Linux 或 macOS,以及一个简单的 Windows 应用程序。现代虚拟化软件甚至不限制您只能运行一个并行操作系统,而是可以并行运行多个系统。限制仅由您自己的计算机硬件定义。现代 IT 中虚拟化技术的应用是无穷无尽的,您将在各个领域找到它们。优势可以从转移 IT 基础设施范式到撤消操作系统更改,这对初学者来说非常理想。

在云计算或现代数据中心中,强大的虚拟化服务器集群同时运行许多不同的操作系统,而不是使用专用服务器硬件。如果您在虚拟化时代之前想要使用 Linux,您需要访问专用的物理计算机。此外,大多数初学者在开始使用 Linux 时会多次搞砸他们的新 Linux 安装。通常,这种系统更改可能很难重新启动,唯一有效和有用的后果就是在卡住时重新安装完整系统。虚拟化摆脱了所有这些问题。由于其强大的功能,例如克隆、拍摄快照或创建图像,我们将在整个章节中方便地使用它来处理所有示例,这也消除了破坏某些东西的恐惧。目前,桌面和非图形服务器环境都有广泛的不同虚拟化产品可供选择,无论是商业还是开源领域。

在核心级别,所有这些不同的虚拟化产品都具有相同的基本功能和共同的定义,我们需要在深入研究之前搞清楚。当我们谈论在物理机操作系统的同时运行的操作系统时,我们将从现在开始称它们为 VM 或虚拟机。在同样的上下文中,运行虚拟化软件的主要操作系统称为主机系统或 hypervisor。在此主机上运行的 VM 称为客户系统或客户。复制和克隆是虚拟化的最重要功能之一。这可以节省宝贵的时间,例如,如果您需要另一台相同的机器或使您的工作更具可移植性。只需将图像复制到笔记本电脑或不同的数据中心,您就完成了。虚拟机的便携式副本也称为图像。另一个令人敬畏的功能是拍摄虚拟机的快照。拍摄这样的快照只需要几秒钟,但会保存虚拟机在任何给定时间点的完整和当前状态。如果您想保留虚拟机的特定版本,以便以后恢复,这将非常有用。不同虚拟化产品共有的另一个特性是支持的网络模式类型。

虚拟机连接到网络的不同模式可以如下:

  • NAT:您的虚拟机的所有传入和传出网络流量都将通过主机网络适配器进行。这意味着该虚拟机在当前网络中不可见,我们只能看到主机的 MAC 和 IP 地址。

  • 桥接:这种网络模式意味着虚拟机会像正常的物理机一样暴露自己并连接到周围的物理网络,具有自己独特的 MAC 地址。此网络中的 DHCP 服务器将为该机器提供与主机机器不同的 IP 地址。

  • 主机模式:这意味着虚拟机只能与其主机通信并对其可见,而不能与网络的其他部分通信。

  • 特定虚拟网络:这是一个非常好的功能,您可以在周围物理网络之外定义私有和隔离的子网,并将虚拟机关联到它。这可能很有用,以便只有虚拟机可以看到并与其他在同一虚拟网络中的机器进行通信。

安装 VirtualBox 和 CentOS

在本节中,我们将向您展示如何安装名为 VirtualBox 的免费虚拟化软件,然后创建一个新的 CentOS 7 虚拟机。我们还将完成非常重要的后安装任务,这些任务将在接下来的部分中执行。安装 VirtualBox 非常简单。可用于每个主要操作系统的可执行安装程序。安装 VirtualBox 的步骤如下:

  1. 打开你喜欢的网页浏览器,导航到www.virtualbox.org/。现在,在主页上清晰可见的下载按钮上点击。

  2. 选择你选择的目标主机操作系统。在我们的例子中,我们将选择 Windows。

  3. 点击 Windows hosts 开始下载。同时,不要忘记下载你可以在同一下载页面上找到的 VirtualBox 扩展包。

这是一个可以提供更好的 USB 支持等其他有用功能的软件包。下载完成后,打开下载的安装程序运行并使用默认设置进行安装。

现在,让我们在 VirtualBox 中创建一个新的 CentOS 7 虚拟机。为了这样做,我们首先需要从官方 CentOS 网站(www.centos.org/)下载 CentOS 7 Minimal ISO 文件版本 1611。这个文件只包含运行非图形 Linux 服务器所需的最重要的软件包,这正是我们想要的。

创建一个新的 CentOS 7 虚拟机的步骤如下:

  1. 打开一个网页浏览器,导航到www.centos.org/。导航到 Get CentOS Now | Minimal ISO。

  2. 在下一个屏幕上,选择靠近你当前位置的下载 URL 以获得更快的下载速度。我目前位于德国,所以我的实际下载 URL 很可能与你的不同,如果你在其他地方的话。

  3. 等待下载完成。

在一台现代和快速的计算机上,你可以在几分钟内在 VirtualBox 中安装一个完全工作的操作系统,比如 CentOS 7。

  1. 在你的系统上运行 VirtualBox。现在,让我们重现以下步骤来安装我们的第一个 CentOS 7 虚拟机:

  2. 点击新建按钮创建一个新的虚拟机。如果你将虚拟机名称输入为CentOS 7,VirtualBox 将会正确识别另外两个字段,类型和版本,为 Linux 和 Red Hat (64 位)。

  3. 点击下一步按钮继续下一步。

  4. 现在,选择你的虚拟机需要多少内存或 RAM。如果你不想让主机系统出现性能问题,保持在内存大小窗口中显示给你的绿色区域内。

  5. 对于一个基本的无头服务器,也就是一个非图形服务器,建议至少有 2GB 的 RAM。不用担心,你以后也可以更改这个设置。

  6. 点击下一步按钮,在下一个屏幕上保持默认设置不变。现在,点击创建。

  7. 选择 VDI 选项,然后点击下一步。现在,在这个屏幕上,保持动态分配选项。点击下一步。

  8. 在下一个屏幕上,将虚拟硬盘大小加倍到16GB,因为8GB对我们的工作来说太少了。

  9. 最后,点击创建按钮创建一个新的空虚拟机,准备安装 CentOS 7。

现在,让我们在空的虚拟机中安装 CentOS 7:

  1. 选择我们的 CentOS 7 服务器虚拟机,然后点击绿色的启动按钮启动它。在这里,我们下载的 CentOS 7 ISO 文件将被 VirtualBox 用作虚拟 CD-ROM,这是它可以被引导或启动的地方。

  2. 点击小文件夹符号,导航文件浏览器到你下载的 CentOS 7 Minimal ISO 文件,然后点击开始按钮。

  3. 现在,你的虚拟机将呈现一个基于文本的启动菜单,在这里我们将使用键盘上的上箭头键选择安装 CentOS Linux 7,然后按Enter开始安装程序。

  4. 等待一段时间后,你将看到第一个图形安装屏幕。

在我们第一次点击或在 VM 窗口中输入之前,我们需要知道一旦进入,我们如何切换回主机系统。如果你在客户窗口上点击一次,一个弹出窗口将显示告诉你如何来回切换控制。

  1. 选择安装程序语言。在我们的例子中,我们使用了默认的英语。

这不是你的 CentOS 7 安装语言。我们将在下一个屏幕上设置这种类型的信息。

  1. 点击“继续”。现在,我们在主安装屏幕上,可以自定义我们的安装。您需要等待直到所有项目都已加载。

在这里,所有标有感叹号的项目都需要在我们继续安装之前完成,但您也可以在此处进行可选设置,如设置位置信息。

  1. 接下来,我们需要设置安装目标。单击“安装目标”。由于我们使用的是空的 VM,我们将使用整个硬盘进行安装,这是默认设置,因此只需单击位于屏幕左上角的“完成”。

  2. 在实际安装之前,让我们快速在这里启用我们的以太网网络卡,这样我们就不必在安装后使用命令行来执行此操作。如果您在代理后面,您也可以在此菜单中添加此类信息。如果准备好了,请单击“完成”。

  3. 现在,让我们点击“开始安装”。在安装进行中,为管理员或 root 帐户设置一个强大和安全的密码,该帐户具有对系统的所有权限和控制。设置好强密码后,单击“完成”。

  4. 现在,从同一屏幕上,您可以为日常工作创建一个普通用户帐户。

任何安全的 Linux 系统的第一条规则是除非必须,否则不要使用 root 用户。

  1. 创建新用户帐户后,单击“完成”。

  2. 现在等待安装完成。安装完成后,单击“重新启动”按钮重新启动系统。

  3. 在启动屏幕上按Enter键将始终选择并使用最新的内核。

  4. 现在,等待直到您在此窗口中获得登录屏幕,这也称为我们的服务终端

  5. 使用您在安装过程中设置的root用户和密码登录。

  6. 然后,在终端中输入以下命令并按Enter键:

# yum update -y    

此命令将安装可用的所有最新软件更新,因为安装程序媒体中没有包含它们。

如果在运行此命令时出现新错误,则您的互联网连接可能出现问题,请在主机系统上排除互联网连接故障。

如果 CentOS 7 系统的核心(称为内核)已更新,我们需要重新启动系统。因此,在终端中输入reboot,然后按Enter。重新启动后,再次按Enter键,然后等待登录屏幕加载并重新登录。

接下来,我们输入另外两个命令,这将清除所有空闲空间,以便我们可以创建系统的较小备份映像。现在,在终端中使用管理员/根访问权限输入以下命令:

dd if=/dev/zero of=/dd.img; rm -f /dd.img    

现在,按Enter键。此命令将通过创建一个包含仅零的大文件来覆盖 Linux 文件系统的所有空闲空间,直到磁盘已满。这将需要一些时间,因此您需要耐心等待。如果此命令输出一些文本,则已完成。您可以忽略错误输出,因为这是预期行为。

最后,为了设置 SSH 端口转发,我们需要记下 VM 连接的网络适配器的实际 IP 地址。运行以下命令:

ip addr list   

Enter,在输出行中输入 IP 地址,即在单词inet之后的值。在我们的示例中,它是10.0.2.15

要关闭 VM,请使用以下命令,然后按Enter键:

shutdown -h

使用 VirtualBox

在本节中,我们将学习正确使用 VirtualBox 所需的最重要步骤。请注意,这里讨论的大多数设置可以直接从 VirtualBox 转换为任何其他桌面虚拟化软件,如 KVM、VMware、Workstation 或 Parallels Desktop。

让我们按照以下步骤导出 VM 映像:

  1. 将光标移动到 Oracle VM VirtualBox Manager 屏幕的左上角。在这里,单击“文件”菜单,然后在下拉菜单中选择“导出设备...”。

  2. 在同一个下拉菜单中,您会找到另一个名为“导入设备...”的菜单项,它允许您在创建图像文件后导入图像文件。

  3. 现在,点击“导出设备...”开始该过程。

  4. 在“导出虚拟设备”屏幕上,选择要创建镜像的虚拟机,然后点击“下一步”。

  5. 现在,在存储设置屏幕上保持默认设置不变,然后再次点击“下一步”。

  6. 同样,在设备设置屏幕上,我们不想更改任何内容,所以点击“导出”按钮开始该过程。这将需要一些时间,所以您需要耐心等待。

  7. 导出过程完成后,让我们来看看生成的文件会是什么样子。

  8. 转到 VirtualBox 导出文件的位置(通常在“文档”文件夹中找到),然后右键单击并选择“属性...”选项查看文件的属性:

正如您所看到的,导出的 VM 大小超过 600 MB,这非常棒。现在可以将此镜像文件复制到备份位置,或者转移到另一台机器或数据中心进行运行。在使用 VM 之前,我们还应该在安装后立即从当前状态创建一个快照,这样我们可以在需要时恢复到现状。

按照以下步骤创建 VM 的快照:

  1. 选择适当的 VM,然后点击标记的 VM 的“快照”选项。给它一个合适的名称和一个可选的描述。

  2. 我们要做的下一件事是创建 VM 的一些精确副本,这样我们就有了多个 CentOS 7 服务器。为此,请右键单击 VM,然后选择“克隆...”选项。给它一个合适的名称,并标记名为“重新初始化所有网络卡的 MAC 地址”的选项,这样它将被视为我们网络中的唯一机器。

  3. 在克隆类型窗口中,选择“完全克隆”,然后点击“下一步”继续。现在,点击“克隆”按钮,同时保持默认选项选中。

  4. 重复之前的步骤,创建另一个完全克隆的 VM。

现在,让我们演示使用快照的威力。在做一些冒险之前,应该始终先拍摄一个快照。例如,启动我们的一个 CentOS 7 VM 并登录系统。现在,让我们想象我们想要在/boot目录上工作,那里存放着 Linux 内核。这是一个关键目录,因此在继续之前创建当前 VM 状态的快照是一个好主意:

在上一张截图中,您可以看到我犯了一个严重的错误。我完全删除了整个内核目录,所以目录现在是空的。如果我现在重新启动系统会发生什么?让我们看看:

如前面的截图所示,我没有内核无法启动,系统现在完全无响应,这真的很难修复。对于这个问题,最好的解决方案是恢复到上一个快照的状态。

执行以下步骤将 VM 状态恢复到上一个快照:

  1. 首先关闭 VM。现在,选择您选择的快照,然后点击“还原”按钮。这将询问您是否要创建当前状态的快照。如果我们不想这样做,那么我们点击“还原”按钮,如下截图所示:

  1. 现在,您可以启动 VM。如下截图所示,我们即将执行“删除”命令:

  1. 如果我们再次启动机器,所有问题都消失了,我们回到了删除内核文件之前的状态。建议经常使用快照功能,因为它可以节省宝贵的时间。

  2. 最后,如果需要的话,我们可以轻松调整虚拟机的硬件参数,如下面的截图所示:

在进行任何硬件更改之前,请确保关闭虚拟机,然后继续。

您只能在机器的一部分上进行此操作。选择您选择的虚拟机,然后点击“设置”,然后点击“系统”,在那里您可以调整内存。此外,您还可以在这里调整虚拟进程。点击“显示”以更改视频内存设置。在存储下,您可以附加虚拟硬盘或创建新的硬盘。在网络下,您可以为您的虚拟机创建新的网络适配器,并在不同的网络模式之间进行选择。在 USB 下,选择 USB 2.0(EHCI)控制器,这样我们就可以正确地将物理 USB 设备连接到我们的虚拟机上。

通过 SSH 连接虚拟机

通过 VirtualBox 用户界面使用新的 Linux 操作系统的终端窗口将使您开始,并可用于配置新服务器的最基本设置。访问服务器命令行的更便捷、高效和专业的方法是使用终端仿真器程序和 SSH。终端仿真器是在您的操作系统的单独窗口中运行的外部程序。它可以与您服务器的主要黑白终端完全相同地使用:

它具有许多便利的功能,例如从操作系统窗口中轻松复制和粘贴剪贴板,以及自定义字体大小和颜色。此外,它使用选项卡进行更好的导航,但是您如何与这样的终端仿真器从您的服务器进行通信呢?这是通过客户端-服务器连接完成的。在您的主机上运行的终端仿真器将被拒绝;我们将连接到 CentOS 7 服务器上执行命令。可以使用 SSH 进行这种连接。这是一种远程访问和使用 Linux 服务器的系统。它使用强加密进行安全连接。由于 SSH 是任何 Linux 服务器上通信的最基本核心服务之一,因此它已经安装并在 CentOS 7 上启用。我们所需要做的就是在我们的终端仿真器程序中运行一个 SSH 客户端,它可以连接和与运行 Linux 和 macOS 上的 SSH 服务的任何服务器进行通信。

在 Windows 上,您需要安装 PuTTY 程序,其中不仅包含 SSH 客户端程序,还是一个完全操作的终端仿真器。在我们可以访问 CentOS 7 SSH 服务之前,我们需要使其正确的网络地址可用于运行终端仿真器的主机系统。用于客户端和服务器之间通信的正确网络连接始终包括 IP 地址或域名,以及特定端口号。域名或 IP 就像一个房子号码,而端口号就像房子里确切的公寓号码。我们总是需要这两个值来正确传递网络数据包。默认情况下,任何客户虚拟机的端口对外部主机系统不可用,因此我们首先需要使用称为端口转发的功能来创建主机和客户之间的链接。SSH 默认运行在端口22上,但非管理员用户或路由用户不能使用低端口号进行转发。由于我们以普通用户身份运行 VirtualBox,因此我们需要将端口22转发到高于1024的用户端口。

在我们的示例中,我们使用端口2222。要做到这一点,请执行以下步骤:

  1. 选择您选择的虚拟机,然后导航到“设置”选项。点击“网络”选项卡。

  2. 探索高级选项。现在,点击“端口转发”按钮创建新的端口转发规则。

  3. 在“端口转发规则”窗口中,单击“添加新的端口转发规则”按钮。现在,使用127.0.0.1 IP 作为主机 IP 部分,这是本地主机的 IP,然后是主机端口2222。对于客户端 IP 部分,输入10.0.2.15,客户端端口22,如下截图所示:

  1. 单击“确定”以创建此规则。

  2. 对我们之前克隆的另外两个 CentOS 7 VM 重复相同的操作。确保使用不同的主机端口,以便为每个主机创建不同的网络端点连接。

  3. 第一个 CentOS 7 VM 服务器现在可以使用 IP127.0.0.1和端口2222访问,第二个使用端口2223,第三个使用端口2224。现在,启动所有三个 CentOS 7 VM。

  4. 打开您喜欢的终端仿真器,例如 xterm、GNOME 终端,或者(就像我例子中的那样)Xfce4 终端。

  5. 要使用终端仿真器登录到您的第一个 CentOS 7 服务器,请使用 root 凭据。在终端窗口中输入以下命令:

ssh -p space 2222 root@127.0.0.1\.     
  1. 按下Enter键。此命令将把您的本地 SSH 客户端连接到 IP 地址127.0.0.1上运行的 SSH 服务器,端口为2222,该端口被重定向到 VM 的网络地址10.0.2.15上的端口22

  2. 如果首次登录服务器,终端会提示输入yes

  3. 现在,输入您在安装过程中设置的 root 用户凭据。

  4. 现在,我们可以像在真实的终端屏幕上一样工作和输入命令。我们还可以使用另外两个端口登录另外两个 CentOS 7 VM。

  5. 要退出 SSH 会话,请输入exit并按Enter

  6. 在 Windows 系统上,您可以使用名为 PuTTY 的免费程序执行完全相同的操作。

  7. 只需打开 PuTTY 图形用户界面,输入 SSH 服务器的 IP 地址127.0.0.1,并使用端口2222进行连接。现在,使用 root 帐户访问 VM。

  8. 设置端口转发后,最简单的方法是使用 Mac 或 Linux 上可用的免费 SCP 程序。在 Windows 上,您需要下载 PSCP。

  9. 从 CentOS 7 VM 客户机下载名为/etc/passwd的文件到主机上的当前目录,使用 root 用户,输入以下命令:

scp -P 2222 root@127.0.0.1:/etc/passwd .    
  1. 当要求时,输入您的 root 密码。现在我们可以在本地查看文件:

  1. 另一种上传名为my-local-file的本地文件到服务器,并输入scp -P 2222 my-local-file root@127.0.0.1:~

  2. 按下Enter,输入您的 root 密码。文件现在已上传到服务器的/home文件夹中,该文件夹由~指定。

总结

在本章中,我们介绍了 Linux 和 VirtualBox 的基本概念。我们首先了解了操作系统的工作原理,然后向虚拟化方向发展。接下来,我们介绍了 VirtualBox 和 CentOS 的安装。然后,我们学习了如何使用 VirtualBox 并使用 SSH 连接它。

在下一章中,我们将了解命令行的工作原理。

第二章:Linux 命令行

在本章中,我们将向您介绍开始使用 Linux 命令行时最基本的概念。这是一个非常强大和高效的工具,您可以使用它执行在 Linux 中通常需要的各种操作。大量的快捷方式和技巧将帮助您更有效地导航命令行。

在本章中,我们将带您了解以下内容:

  • Shell 通配符

  • 重定向和管道

  • grepsedawk命令

  • 在 Linux 系统中导航文件和文件夹

介绍命令行

在本节中,您将学习如何运行 Linux 命令行程序以及命令行的基本结构是什么。您还将了解程序选项和参数以及它们对自定义命令的重要性。

当我们说 Linux 命令行时,我们真正指的是shell。重要的是要知道 shell 不同于终端仿真器。终端是一个屏幕或窗口,让您访问 Linux 服务器的输入和输出。shell 只是在服务器上运行的一个程序,就像任何其他命令一样,它等待、解释、处理、执行和响应用户输入的命令。

首先,打开一个新的终端仿真器,并使用 SSH 登录到您的 CentOS 7 服务器,就像我们在第一章中学到的那样,Linux 简介。使用您在安装过程中设置的普通用户账户登录,因为正如我们之前所说的,除非必须,永远不要使用 root 用户。在我的示例中,用户名是olip

成功登录到服务器后,一个重要的程序已经自动启动,这个程序叫做 shell,我们一直在使用它。事实上,当我们谈论 Linux 终端时,我们真正说的是 shell。存在几种 shell 变体;在 CentOS 7 上,默认使用Bash,或者Bourne Again Shell。当 shell 启动时,您会注意到以美元符号($)结尾的行,这被称为 shell 提示符。

在我们的示例中,它为我们提供了一些有用的信息:登录用户名和当前所在的目录。波浪号是一个特殊字符,表示家目录,这是登录时的默认目录。在 shell 提示符之后是光标,即下划线字符,这是用户可以输入文本的地方,然后由 shell 处理和执行。但是只有当输入以Enter键结束时,用户输入才会被 shell 处理和执行。如果出现任何错误,只需按下退格键删除最后一个字符。我们将在本章学习的第一个有用的命令是如何退出系统。

在 Linux 终端上,这个命令注销当前用户并返回到登录屏幕:

  1. 打开 Linux 终端,输入logout命令,然后按Enter键。

  2. 然而,如果您在使用 SSH 连接时执行相同的操作,它的效果与我们在上一章学到的exit命令相同。

  3. 让我们再次尝试登录到 CentOS 服务器。

  4. 让我们尝试一个简单的命令;输入date并按下Enter键。这是一个打印当前日期时间值的命令:

如您所见,如果 shell 已经执行完特定命令并准备接受用户的新输入,新的 shell 提示符将出现在新行中标志着它的准备就绪。现在,输入cal并按Enter。这个命令打印出当前月份的一个漂亮的表格视图。

如果任何命令类型的第一个字符在 shell 中以井号键前置,当按下Enter键时,该命令将不会被执行。

一个典型的 Linux 系统,比如 CentOS 7,包含了默认安装的数百个不同的命令。如果你只能输入纯命令而没有其他东西,我们在 shell 中的工作将非常有限和静态,你将无法正常工作。因此,我们需要一种方法来自定义我们的命令或在执行过程中改变默认行为,提供更多的信息。但是我们该如何做呢?

进入命令行选项和参数的力量。首先,我们需要讨论 shell 中命令的一般结构,它最简单的形式是COMMANDNAME OPTIONS ARGUMENTS。命令名称是要启动的命令的名称。注意,在 Linux 中命令名称是区分大小写的。输入whoami然后按Enter。这个命令将打印出在 shell 中工作的当前用户的名称。由于 Linux 是区分大小写的,这个命令不能使用大写字母启动,比如每个版本都指代不同的命令。在这里,我们也将看到为什么 shell 是一个如此有用的程序。它不仅听取和解释命令,而且在出现问题时还会显示有用的错误消息,比如系统中找不到一个命令。通常,在 Linux 上所有标准的 Bash 脚本命令都是用小写字母编写的。要获取一些可用命令的列表,输入ls /bin。现在,让我们继续讨论 shell 中可用的最基本的命令之一。输入ls并按Enter键。这个命令列出目录中的文件。如果没有提供更多的信息,它将打印出我们当前所在目录中所有可见的文件:

正如你所看到的,一个 shell 命令也可以包含附加到命令名称的选项和参数,并且用空格分隔开。这意味着如果你想提供至少一个选项或参数,那么在命令名称后至少需要一个空格。首先,让我们谈谈命令行选项。它们的目的是影响命令的行为。它们也被称为开关标志。没有强制性的标准,但通常任何单字符命令行选项以单破折号开头,而较长的选项名称有两个破折号符号。此外,如果你想提供多个单字符命令行选项,对于大多数标准的 Linux 命令,你可以直接连续写它们。值得知道的是,单字符命令行选项通常是描述它们含义的缩写:-d可能代表目录,-x代表排除,等等。

我们已经知道,ls命令没有任何其他选项时会给我们当前目录中所有文件的列表。如果你输入ls -a并按Enter,你就运行了你的第一个带命令行选项的命令。a开关代表所有,这通过给你一个包括隐藏文件在内的所有文件的列表来影响ls的默认行为,这些文件在 Linux 中以当前目录中的前导破折号开头。现在,让我们输入ls -alth并按Enter键来看结果:

这通过使用我们刚刚讨论的-a标志更进一步影响了命令的默认行为,并且还使用了-l开关,代表列表,它以列表格式打印出所有文件,包括更详细的信息,比如创建日期。-t开关代表时间,它按修改日期对文件列表进行排序,最新的条目首先出现,-h代表人类可读,它将以更易读的形式打印出文件大小,使用MB而不是字节来表示文件大小。

通常,命令行选项可以有与之绑定的参数。除了选项,我们还有命令行参数,也称为参数。这是在命令启动时输入的任何动态或自由文本信息,它不是选项,并在执行期间被传递给命令。典型的例子是命令在执行期间想要处理的文件名或目录。参数也由空格分隔。

键入echo Hello并按Enter键:

在上一个命令中,Helloecho命令的参数,而不是选项。echo命令是最基本的 shell 命令之一。它只是将给定的参数打印回命令行。正如我们将看到的,这对于测试 shell 功能非常理想,比如通配符,我们稍后将在本节中学到更多关于它的知识。现在让我们在终端中键入ls -al /boot /var并按Enter键,以查看类似以下的结果:

在这个例子中,我们第一次使用了命令行选项和参数。ls命令使用al选项执行,并且参数是/boot/var。这将打印出/boot/var目录中的所有文件,包括隐藏文件,以详细列表视图显示。如前所述,通常情况下参数与特定选项绑定,例如tar命令,我们稍后会讨论。当您需要处理输入文件时,您必须直接在-f选项之后指定,而不是其他地方,或者简而言之,输入文件参数与-f选项绑定。这种方法是不正确的,会产生错误。

文件通配符

在这一部分,您将学习 shell 扩展的工作原理,以及我们如何使用文件通配符来使我们在使用处理大量输入文件的命令时更加轻松。我们将讨论所有现有和可用的 shell 通配符字符类,并为每个字符类展示重要的用例和示例。在处理将文件或目录名称作为参数的命令时,比如ls命令,学习文件和目录通配符非常有帮助。这些是在 shell 中键入的特殊字符,其行为与普通字符不同。所有通配符字符都将在任何命令使用它们作为参数之前被 shell 替换为与字符模式匹配的文件列表。这是一种简化处理文件的表示法,特别是在处理大量需要输入和处理的文件时。使用文件通配符可以节省大量时间,因为多个文件可以由一个字符表示。将这些特殊字符替换为文件组列表的概念也称为shell 扩展。有几个可用的通配符字符,我们可以使用它们来创建非常复杂的文件列表选择。

通配符字符是通配符、问号、感叹号、方括号和破折号。尽管它们看起来行为非常相似,但 shell 通配符和正则表达式并不相同,这两个概念也不可互换。这意味着您不能将正则表达式应用于通配文件,反之亦然。我们将在本章的后续部分中学习更多关于正则表达式的知识。最重要的通配符字符是通配符字符。它将匹配特定目录中任意数量的任何字符文件名,有一个例外,它不匹配以点开头的文件,这一点您可能已经注意到了在 Linux 中查看隐藏文件时。如果您使用通配符字符与以点开头的文件并按Enter键会发生什么?让我们看一个例子。正如我们之前展示的,我们可以使用echo命令在终端中打印出随机文本。

让我们首先切换到另一个目录。输入cd /etc并按Enter。现在,输入echo *并按Enter

在上一个命令中,在第一步中,shell 将通配符替换为当前目录中的文件列表,并按照规则用空格分隔打印出来,然后显示包含任何字符的所有文件和目录,但不显示以点开头的文件。使用echo是测试您的通配模式是否完全匹配您想要的内容的完美方式,然后将其应用为真实的命令行参数。您可以将通配符与任何其他静态字符混合使用,使文件过滤更严格。输入echo pa*并按Enter。这将匹配所有以小写p开头,后跟a,后跟任何其他字符的文件。或者输入echo *.d并按Enter。这个例子找到所有具有.d文件扩展名的文件:

您甚至可以定义一个更严格的模式,例如,输入echo li*.conf并按Enter。这个通配模式将匹配当前目录中以小写l开头,后跟i,后跟任何其他字符的所有文件,但只有那些具有.conf文件扩展名的文件。我们可以在接受文件选项列表作为参数的任何命令中使用文件通配,比如ls命令。

例如,使用通配模式li *.conf作为ls命令的命令行参数,会给我们一个详细的列表,列出了这个模式匹配的所有文件。同样,重要的是要理解,我们并没有将通配模式输入ls命令,ls在程序执行期间也不会内部扩展文件。事实上,shell 在第一步中将通配符扩展为文件列表,然后将此列表作为ls命令的参数。

我们将使用ls -d选项来不显示目录内容,默认情况下会显示;这是因为 shell 通配不区分文件和目录。

在终端中输入ls -d rc?.d。这将为您列出所有只有一个随机字符作为第三个字符的文件。接下来,按照以下方式输入ls -d krb5.conf??命令:

正如您所看到的,问号也可以多次使用。这将获取所有具有扩展名的两个随机字符的文件,只有这些文件。我们将要学习的最后一种通配符是方括号,它定义了特定位置上允许字符的范围,例如,输入ls -l sub[ug]id。这将扩展为以sub开头并且第四个字符为ug,后跟id的所有文件列表:

正如我们将在下面学到的,我们可以将括号与其他通配符字符混合使用。输入以下ls命令参数:

ls /bin/[mM]ail*  

这将扩展为bin目录中所有邮件程序的列表,包括大小写。我们稍后将学习更多关于bin目录的知识。您还可以使用数字来表示范围;在终端中输入ls -d rc[01234].d命令:

在我们的例子中,这将扩展为rc0.drc1.d等。如果您有连续的数字或字母范围,就像在最后一个例子中一样,您还可以使用减号来进一步缩短您的通配表达式。例如,输入ls /bin/m[a-z] [a-z]。这将给我们bin目录中以m开头的所有三个字母命令名称。

还有另一个有用的通配符,即感叹号,它可以在括号中使用,定义在扩展结果中不得出现的内容,例如,ls -d rc[!256].d

这表示我们不希望扩展第三个字符为256的文件。这也适用于括号内的连续范围,例如,ls -d rc[!3-6].d

您已经学到了关于 Linux 中隐藏文件的三件事。它们的文件名以点开头,通配符通配字符会忽略它们,ls默认情况下不显示它们;因此,它们被称为隐藏。要显示主目录中的所有隐藏文件,我们使用ls命令的-a选项。您会看到主目录中有几个隐藏文件,例如.bashrc文件:

但是在您的目录中还有两个特殊文件,名为[.]和[..],我们将在本章后面学习这两个特殊文件是什么。如果您只想显示当前目录中的隐藏文件而不包括这两个点文件,您需要输入什么?有了您现在拥有的所有知识,这应该很容易实现,下一行现在应该对您有意义。因此,输入ls .[!.]*。但这也会列出目录内容。为了不列出目录内容,使用ls -d标志,因此命令将是ls -d .[!.]*

在本节中,我们讨论了关于 Linux shell 通配符的一切。请记住,通配符字符匹配任何位置的每个文件名字符。对于这个规则有一个例外是非常重要的:它不匹配以点开头的文件名,这在 Linux 中称为隐藏文件。问号也是如此,但只在单个位置上;它也不匹配以点开头的文件名。括号在括号之间定义的单个位置匹配特定字符。当具有连续允许字符时,您还可以使用破折号符号。要匹配特定位置上除了一组字符之外的所有内容,请在括号中使用感叹号。

引用命令

正如我们在前一节中学到的,shell 有一系列特殊字符,在 shell 中具有特殊含义并触发某些功能,例如使用通配符字符作为文件名。但是,特殊字符比我们之前向您展示的还要多。如果您想使用包含问号符号的文件名等特殊字符,您将遇到问题,因为 shell 总是首先尝试对特殊字符应用特殊操作,因此它们不会像正常的文件名字符一样工作。解决方案是使用各种方法禁用这些字符的所有特殊含义,例如引用,以便我们可以将它们视为任何其他普通的文字字符。正如您现在所知,在 Linux Bash shell 中有一些特殊字符,例如* # [ ] . ~ ! $ { } < > | ? & - / , ",它们对 shell 具有特殊含义,并且与普通字符不同。但是,如果您想要使用包含其中一个特殊字符的文件名或目录作为参数,该怎么办?另外,如何处理名称中带有空格的文件名,这也可以被视为特殊字符?

例如,如果您的目录中有一个名为My private Documents.txt的文件,您如何将其用作命令行参数?如果您将其与ls命令一起使用,由于空格是命令行参数分隔符,shell 无法将其视为一个独立的文件。相反,它认为您提供了三个名为MyprivateDocuments.txt的不同文件:

另外,如果你想使用包含特殊字符的文件,比如感叹号,例如,如果你有一个名为!super!file!.txt的文件,在 Linux 中这是一个有效的文件名?如果我们尝试将这个文件名作为命令行参数使用,它无法找到这个名称的文件,因为它包含特殊字符,而 shell 以不同的方式处理它们。或者如果你想要echo一些文本,单词之间有多个空格?正如我们所学到的,空格也是一个特殊的 shell 字符,用于分隔命令行参数。

在刚才展示的示例中,我们需要找到一种方法来禁用 shell 扩展并停止 shell 处理特殊字符。在参数中禁用 shell 扩展有两种简单的方法,即引用和转义。将特殊字符和空格放入单引号中将防止 shell 扩展并将所有可能的字符,包括特殊字符,视为普通的字母数字字符。在单引号中,任何东西都不会被 shell 扩展;对于大多数特殊字符,这也适用于双引号,但有一些例外。

在下面的截图中,两个示例有效,但其他示例不起作用,它们会得到特殊处理:

另外,如前面的截图所示,美元符号也保持特殊,这经常用于在引用时需要 shell 扩展环境变量。如前所述,单引号将禁用所有特殊字符。你可以通过使用反斜杠键来做到同样的效果,在 shell 中,它也被称为转义字符,几乎与引号完全相同,但它只会禁用 shell 扩展和下一个特殊含义,只有在反斜杠键之后的下一个字符:

正如你所看到的,基本上是一样的。通常,转义字符用于通过在每一行中转义或禁用换行字符来创建清晰的多行命令行调用。反斜杠字符的另一个用例是在处理以破折号开头的文件等参数时使用它,因为这经常会让 shell 感到困惑,因为它会将任何破折号符号解释为选项。

例如,如果我们想创建一个名为-dashy.txt的空文件,这是行不通的,因为命令行混淆了并认为文件名是一个单字符选项列表。在这里,我们可以使用转义字符来摆脱破折号符号的特殊含义。对于以破折号开头的参数,一些命令,如lstouch,还有另一个很棒的功能,双破折号,它标志着选项列表的结束。因此,为了将您的 dashy 文件作为参数而不是选项进行处理,我们还可以输入nano -dashy.txttouch '-dashy.txt'命令。

正如你已经学到的,shell 中存在许多特殊字符,例如 shell 通配符字符或感叹号。如果你想使用这些字符,而不是扩展文件列表,而是在文件名或其他文字命令参数中使用它们,该怎么办?你需要禁用它们。使用单引号将禁用所有特殊字符,并且在 shell 中工作时是首选的方式;它适用于几乎所有日常引用用例。在使用双引号时,大多数特殊字符会被禁用,但不是全部,比如环境变量的 shell 扩展。因此,这种方法对包含普通字符和环境变量值的文本创建非常有用。反斜杠或转义字符将仅禁用接下来的字符的任何特殊含义。

获取帮助

在我们开始教您如何使用 Linux 命令的各种文档形式获取帮助之前,我们首先必须学习如何阅读默认命令语法文档。Linux 中提供的大多数标准 shell 命令都遵循描述其用法的统一格式。之后,我们将向您展示如何获取帮助。

在使用 Linux 命令行时,获取帮助并查找信息和文档非常重要,因为命令行可能非常复杂,没有人知道并且能记住一切。在每个 Linux 系统上,有几种可用的方式可以获取帮助,具体取决于您需要了解的信息级别。在本节中,我们将利用不同的文档来源。

在前面的部分,您已经学习了 Bash shell 命令的一般结构以及您需要了解的关于命令选项和参数的一切,但通常这是不够的。对于许多 shell 命令,选项和参数的特定结构非常复杂。一个命令可以绑定到特定位置,其中一些可能是强制性的或可选的。此外,选项和参数可以相互依赖。在 Linux 中,命令的命令行格式描述,包括参数和选项,称为命令用法命令的语法。学习阅读命令用法是 Linux 初学者在开始时需要学习的最基本的技能之一。在 Linux 中描述命令用法的标准方式是命令名称,包含文本的方括号,点和文本,例如,CommandName [XXX]... TEXT。方括号表示其中的内容是可选的。三个点表示点之前的表达式可以重复多次或仅一次。没有方括号的任何单词都是强制性的。

例如,ls命令的一般语法,您已经知道如何使用。从官方ls手册中,可以读取为ls [OPTION]... [FILE]...;这意味着列出文件的命令具有以下用法。它以ls命令名称开头,其他所有内容都在括号中,因此所有选项和参数都是可选的,这意味着您也可以只通过按Enter键来执行ls而不提供任何其他信息。但您也可以提供多个选项或仅一个。此外,我们可以看到参数是FILE类型,这意味着在这个位置需要一个文件或目录。您还可以提供多个文件或目录,或者只有一个或零个,如下面的屏幕截图所示:

另一个例子是copy命令可以通过使用cp命令名称后跟零个或多个选项来运行。cp命令的语法是cp [OPTION]... SOURCE... DEST_DIR。您可以完全跳过选项,但至少一个或多个源目录和一个目标目录是强制性的,并由三个点表示,没有它们您无法运行该命令。例如,仅运行cp而不提供至少两个参数会产生以下错误。正确的用法应该包括所有选项:

现在我们知道如何阅读任何标准命令语法或用法,那么我们如何实际获取帮助呢?正如我们之前所说,有几种可用的方式,即命令帮助选项、man 页面和完整的程序文档。通常,所有这三种类型的帮助都与命令行或程序一起安装,因此首先尝试在相同的机器上本地获取 shell 命令的帮助是一个非常好的习惯。这通常是每个命令的最准确、可靠和最新的信息,并且应该优先于在互联网上进行研究或使用来自具有不同 Linux 版本或系统的其他计算机的文档。

通常在博客或论坛中找到的互联网解决方案对于您特定的 Linux 安装来说太不具体或纯属错误,应始终谨慎使用。永远不要盲目地从互联网上复制和粘贴命令片段。

命令参数、选项和功能可能会随着版本和实现的变化而发生变化,如果错误应用,可能会非常危险。Linux 上有数百个可用的命令,每个命令都有不同的语法。没有人可以记住所有内容,所以让我们首先从获取任何标准 Linux 程序的快速帮助的最简单和最快的方法开始。实际上,大多数程序都有一个特殊的命令行开关,可以在屏幕上打印出其选项和参数的快速摘要,这在大多数情况下就是您需要了解的全部内容。但是,在 Linux 上,帮助或使用标志并不是标准化的,有些命令甚至根本没有这个标志,但大多数工具开发人员遵循使用单字符标志-h或长选项标志--help的规则。

并非所有 shell 命令都有帮助选项,特别是那些非常简单的命令。

现在,如果您需要更多帮助,您可以查看命令手册,Linux 用户通常称之为man 页面。大多数程序都有这样的文档。对于接下来的几个示例,您需要使用您在安装过程中设置的 root 帐户密码安装一些额外的软件。man 页面使用较少的导航,我们将在学习如何查看文本文件时稍后讨论。

以下步骤将帮助您在 Linux 终端中导航到任何命令的手册:

  1. 打开终端,输入man cp以查看copy命令的手册。

  2. 使用Page UpPage Down键向上和向下滚动文档,斜杠(/)可用于搜索文本;在斜杠后面放置任何关键字进行搜索,然后按Enter。例如,/backup

  3. End键搜索 man 页面中的下一个条目。

  4. 要退出搜索选项,请使用Esc键。

  5. 使用小写字母g,您可以滚动到页面顶部,而大写字母G则可以滚动到页面底部。

  6. 您可以按小写字母q退出 man 页面。

当您返回页面顶部时,cp命令的 man 页面被分成不同的主题和标题,如下面的截图所示:

大多数标准的 Linux 命令都遵循这种结构。此外,您可以在这里看到,一些命令可以根据给定的选项和参数具有不同的使用格式。现在,使用q键退出。man命令有一个非常有用的选项,输入man -k,然后在参数后面放置任何感兴趣的定义。这将搜索系统上安装的所有 man 页面,以查找特定关键字。例如,如果您忘记了特定的命令名称,或者需要关于主题或要使用的命令的一般帮助,或者首先查找的位置,这将非常有用。如果您键入man -k copy命令,这将打印出所有与复制有关的命令的 man 页面:

在使用-k标志时,您还会看到搜索结果在 man 名称后面写了一些数字;这些是 man 页面部分,这是另一个非常有用的概念,我们需要了解。Linux shell 定义,例如printf,可以描述的不仅仅是命令行程序,man 页面不仅仅描述命令行工具。在我们的例子中,printf不仅是一个可以由 shell 用户启动的命令行工具,还是 C 编程语言中使用的库函数的名称。man现在为特定 man 名称的类型定义了一套部分编号系统。键入man man将显示man命令的手册文档,并搜索键盘部分,如下所示:

正如我们在之前的截图中看到的,man 命令的 man 页面有九个部分。第一个对我们来说是最重要的,因为我们很可能是 shell 命令的用户。但是,正如你所看到的,第三部分是一个库调用。输入man printf,它会打印printf命令的用法。另一方面,如果你输入man 3 printf,它将打印 C 语言的 Linux 程序员手册。

让我们跳到第八部分,这是为系统管理员编写的xfs_copy命令的手册。除了手册页面外,许多可以在 Linux 上安装或者与系统一起提供的命令,在文件系统中的特定文件夹位置都有额外和高级的文档。对于一些程序,还可以使用特殊的安装包安装额外的文档,我们将在本节后面学习。有时,这些额外的文档包含了如何使用程序的宝贵用法示例;关于内部算法或方法的信息;更改日志和许可信息;作者联系信息;历史记录;错误或限制列表;或者样本配置文件,我们稍后会谈到。

如果你在手册中遇到困难,或者手册对你来说不够,尝试查看你的 CentOS 7 标准文档路径中是否存在你感兴趣的命令的文档文件夹。例如,输入postfix文档文件夹所在的位置。这是一个很好的例子。如果你进入该目录,你会发现很多文本文件格式的额外文档。更多信息请参考以下截图:

使用 less 程序来阅读文件。使用与 man 页面相同的键盘快捷键来浏览文件,例如,输入q退出。

如果你需要更多或高级文档,请查看/usr/share/doc文件夹,看看是否有适合你的内容。

使用 Linux shell

在本节中,我们将学习如何在 shell 中高效工作。我们将介绍一些重要的实践和技术,这将提高你的生产力,使你成为一个更快的 shell 命令黑客。这可以让你成为一个更快乐的人,因为最终,你将能够感到非常舒适地在 shell 中工作。请注意,在本节中,我们将向你展示很多键盘快捷键。学习键盘快捷键就像学习任何其他技能一样,你要慢慢开始,因为一次学习太多新技能会让你感到不知所措,比学习小块的知识更容易忘记。我的建议是从学习前三到四个命令编辑快捷键开始,然后逐日或逐周增加更多。我们将从命令编辑快捷键开始。现在,如果你根本不知道任何命令编辑快捷键,让我们回顾一下你可能已经知道的如何在命令行中输入和编辑文本。

移动光标位置的第一个快捷键是使用左右箭头键,这有助于编辑你输入的文本,在特定位置插入或删除字符。但如果这是在 shell 中所能做的一切,那么在 shell 中工作将会非常低效,因为单个字符的光标移动非常缓慢。此外,每当使用有错别字的命令或者需要以稍有不同的方式重新运行命令时,例如更改一个选项,就需要从头到尾重新输入完整的命令。

为了更高效,让我们介绍一些非常重要的命令编辑快捷键,用于你在 Linux 中的日常工作:

  • 要将光标移动到行尾,使用Ctrl + E

  • 要返回到开头,分别按下Ctrl + ACtrl + ECtrl + A

  • 将光标移动到下一个单词,该单词由空格或特殊字符(如句点、分号或点)定义,使用Ctrl和右箭头键向前移动。

  • 要向后移动一个单词,请按住Ctrl键并使用左箭头键。您还可以使用meta + Fmeta + B来执行相同的操作。

  • 在大多数系统上,就像任何普通 PC 键盘一样,没有元键,因此元键映射到EscAlt键。

在某些终端仿真器(如 Xfce4 终端)中使用Alt键保留用于菜单可访问性。因此,在使用它作为快捷键之前,您必须在首选项中禁用Alt键作为菜单快捷键。

  • 要在当前位置和行的开头之间切换,请两次按Ctrl + XX

  • Ctrl + K删除光标到命令行末尾的文本。

  • 要删除光标到命令行开头的文本,请按Ctrl + U。使用Alt + D删除到单词的末尾。

我们刚刚讨论的所有命令编辑键盘快捷键都是您日常使用中最重要和高效的快捷键,还有很多很多。

要获取所有 Bash 键盘快捷键的完整列表,请执行以下操作:

  • 输入man bash,然后搜索移动命令的部分

  • 在此手册页中搜索Killing

  • 在此手册页中,C键是Ctrl键,M键是元键,破折号表示组合或按住两个键,就像我们之前使用Ctrl + A快捷键所示的那样

例如,C-k代表kill-line,它会删除从光标到行尾的文本。Alt + T用于交换单词,M-u用于将单词大写,M-l用于将单词小写。

现在,让我们继续讨论命令完成快捷键。最重要的命令完成快捷键是键盘上的Tab键。它会尝试猜测并自动完成您即将输入的命令。这非常有用,可以大大加快输入命令的速度,但是在使用此键时不要过度使用,如果没有可用的替代方案,它只能打印完整的唯一命令名称。输入pass并按Tab键;它将自动完成名称passwd,因为没有其他具有完整名称的程序。输入pa并按Tab键;这将给出几个结果,因为找不到唯一的名称。输入yp并按Tab键;这将自动完成为长名称,因为这是唯一的variantTab快捷键默认情况下自动完成命令;要自动完成其他内容,如文件名,请使用Alt + /键。在 Bash 手册中的相应部分中可以找到更多信息。

现在,让我们看一下命令回调快捷键。Linux shell 具有一个非常好的功能,即history命令。这是一个用于存储和检索输入到 shell 中的所有命令的系统。在 CentOS 7 系统上,默认情况下会存储最近的一千个命令。这个数字也可以更改。命令行历史记录是一个非常有用的功能,可以节省时间,不用重复输入,或者查看一段时间前如何执行特定命令。要打印当前历史记录,请输入history并按Enter。如果要从此列表重新执行命令,请使用感叹号和相应的数字。两个感叹号运行历史记录中的最后一个命令。另一种感叹号符号表示可以从历史命令中提取特定参数。这将从history命令中提取第三个参数166,如下图所示:

另一个非常有用的历史功能是回调上一个命令:

  • 要浏览以前执行的历史命令,请按键盘上的上箭头键。

  • 要返回到下一个历史命令,请使用下箭头键。

  • 要在历史记录中搜索命令,请按 Ctrl + R,然后输入搜索关键字。

  • 要循环浏览结果,请再次按下 Ctrl + R

  • 要运行找到的特定命令,请按 Enter 键。

  • 要快速插入上一个命令的最后一个参数,请使用 Alt + 点。

  • 另一个非常有用的功能是手动扩展一行而不实际执行该行,这对于查找错误和框很有用。这可以使用 Ctrl + Alt + E 来完成。

接下来,我们需要知道如何使用程序和进程。首先,我们将讨论如何中止任何正在运行的程序。如果需要退出命令,因为它没有响应或者您犯了一个错误并希望停止它,这一点非常重要。例如,让我们输入 cat 命令,它将永远运行。让我们暂时忽略此命令目前正在做什么。这会使 shell 无响应,因为 cat 永远不会在我们的 shell 前台完成运行并永远运行。为了回到 shell 提示符,以便我们可以再次输入新命令并继续工作,我们需要在其运行时退出命令。在 shell 中,我们可以使用一个特殊的组合键来退出当前前台进程。按下 Ctrl + C

这是一个非常重要的快捷键,应该记住:Ctrl + C

您还可以暂停一个程序,就像暂停其处理并将其放入后台,这样您就可以再次在 shell 中工作。可以按照以下步骤完成:

  1. 按下 Ctrl + Z。如果以后想要继续在前台运行程序,输入 fg 然后按 Enter

  2. 您还可以在暂停时使用 bg 命令将其放入后台。现在程序在后台运行,您可以在前台工作。

  3. 退出后台运行的程序的最简单方法是将其放到前台,然后使用 Ctrl + C 中止它。

  4. 下一个非常有用的命令是按下 Ctrl + L,这将清除屏幕,与 clear 命令具有相同的效果。

  5. 我们将在这里学习的最后一个非常有用的命令是按下 Ctrl + D,这将关闭 Bash shell。这类似于输入 exit 命令。

理解标准流

在本节中,您将了解为什么每个命令都可以使用三个标准流来访问其输入和输出。此外,您还将学习如何使用这些输入和输出流以及如何使用重定向。最后,我们将学习如何使用管道以及它们为何如此重要。Linux 操作系统的一个理念是,每个命令在系统中都有确切的功能,没有多余的,也没有不足的。例如,有一个命令用于列出文件,另一个用于对文本进行排序,还有一个用于打印文件的内容,等等。

现在,shell 的最重要功能之一是连接不同的命令,以创建定制的解决方案和工具来解决各种问题和工作流程。但在我们展示如何将不同的命令连接在一起构建强大的东西之前,我们首先需要知道命令如何使用其输入和输出,以及输入和输出重定向是什么。大多数 Linux 命令在处理数据时遵循类似的模式。我们使用的大多数命令都会获得某种输入,例如,它们读取文件的内容,然后处理这些信息,之后几乎所有命令都会在计算机屏幕上输出某种结果。因为每个命令在 Linux 上都会使用某种输入并返回某种输出,所以为每个命令定义了三个标准通道,并且这些通道对于每个命令都是可用的。它们用于在执行期间操作系统和命令之间的通信。它们被称为标准输入stdin标准输出stdout,以及标准错误stderr

正常程序输出进入stdout通道,而stderr也是一个输出流,可以用于显示和处理在执行命令时发生的任何错误消息。这些也被称为标准流。它们被称为流,是因为数据通过特定通道持续流动,并由命令连续处理或生成,尽管它们有一个开放的末端,这意味着使用它们的命令无法预测数据流何时会停止或结束。现在,我们可以使用特定文件更改stdinstdout的位置;这称为重定向

在这一部分,我们还将解释管道的概念,这是 Linux shell 最基本的概念和主要特性之一,以及如何使用它们。例如,如果您键入ls /var/lib/system/,结果 random-seed 将被打印到屏幕上,因为它默认为每个 Linux 命令的stdout设备。但是,如果您键入cat /var/log/messages,错误消息将打印到同一屏幕上,因为stdoutstderr都连接到同一输出设备,即屏幕。

在 Linux 上,您的物理输入和输出设备,如键盘或屏幕,与其他硬件设备一样,都是通过特殊的系统文件进行抽象和表示的。所有这些特殊文件都驻留在一个名为/dev的系统目录中,也称为系统设备目录。但是我们可以用这样的系统做什么呢?它的美妙之处在于我们可以将命令的输入和输出重定向到除默认键盘和屏幕源或目的地之外的其他位置,这些位置也必须是文件类型。这对将stdoutstderr分开到两个不同的位置也非常有用,特别是在命令产生大量输出时有助于保持概览。

对于输出通道重定向,我们使用大于号(>),对于输入重定向,我们使用小于号(<)。要处理特定通道,如stdinstdoutstderr,我们使用相应的数字012。在使用输出重定向时,期望stdout通道,因此我们不必明确写出它。在 99%的情况下,您只需要重定向stdoutstderr,因此让我们专注于这些示例。

要将命令的stdout流输出重定向到文件,请使用大于号。如前所述,期望stdout通道,因此最后一个命令也可以输入如下:

ls /var/lib/systemd/ > /tmp/stdout-output.txt
ls /var/lib/systemd/ 1> /tmp/stdout-output.txt  

使用card命令打印出刚刚通过重定向到stdout创建的文件的内容。要重定向stderr通道,请使用数字2作为标准流描述符。以下截图显示了先前命令的输出:

正如您所看到的,错误消息已被重定向到一个文件中。要将stdoutstderr重定向到两个不同的文件中,请输入以下截图中显示的命令:

另一种表示法,使用和符号允许将一个通道重定向到另一个通道。要将stderr重定向到stdout通道,请输入以下截图中显示的命令:

有时,您只对一个输出流感兴趣,因此在任何 Linux 系统中都存在一个特殊的设备文件,称为null设备,它会消耗并消失任何被重定向到它的流数据。如果您不希望对任何命令输出任何输出,例如,您可以使用以下截图中显示的命令:

最后,要重定向stdin,您可以使用小于号[<]。例如,这可能非常有用,因为一些可用的 shell 命令可以直接读取文件的内容作为stdin,比如grep命令,我们稍后会学习。

现在,让我们讨论管道。除了将命令的默认输入和输出流stdinstdoutstderr重定向到文件之外,我们还可以使用 shell 管道的概念,将一个命令的输出作为另一个命令的输入。这个系统没有限制,非常容易构建多命令链,为您回答非常复杂的问题。正如之前提到的,这个 shell 功能可以让您创建非常强大的命令管道和工作流程,为各种 Linux 命令行工作创建定制解决方案,并为您回答非常复杂的问题。

要将命令链接在一起,也就是将第一个命令的stdout作为下一个命令的stdin使用,我们使用键盘上的竖线符号[|],在 Linux 中称为管道符号。例如,如果您有一个非常长的目录内容列表,您希望在终端窗口中不断滚动地阅读,您可以使用管道将ls命令的目录内容输出到屏幕上,而不是直接作为文件查看器的输入,就像我们之前学到的那样。通常,管道用于避免中间结果文件,并且没有它们更有效率。这种用法的用例是无穷无尽的,例如,如果我们有一个包含未排序人名的文件,我们可以使用cat names.txt | sort进行排序:

您还可以获得此文件中所有唯一名称的列表。我们将使用唯一命令来执行此操作,该命令仅适用于已排序的列表。因此,我们需要使用cat names.text | sort | uniq进行排序:

您还可以使用单词计数命令行工具来计算唯一行的数量,使用cat names.text | sort | uniq | wc

此文件中有相当多的唯一名称。在管道示例方面,天马行空,例子太多。理想情况下,应该使用root用户帐户运行。请忽略错误。以下屏幕截图显示了文件系统的核心摘要:

另一个有用的管道命令是打印目录中使用的文件。如果您使用的是 Windows 系统,您可能知道一个称为 ZIP 的实用程序,它可以压缩文件。在 Linux 上,您可以做类似的事情,但是这里我们需要两个工具一起工作。对于压缩,我们使用gzip工具。因为gzip只能处理单个文件,所以我们首先需要创建一个将多个文件连接到单个文件的存档。对于存档,我们使用tar命令。因此,要在/tmp目录中创建您的主目录的压缩存档,请首先使用tar命令创建您的主目录的存档:tar -cv /home/olip/ | gzip。存档将输出到stdout流,因此我们将其作为stdin管道到gzip命令。因为 gzip 本身将压缩文件输出到stdout,所以我们将其重定向到文件。压缩与未压缩数据量的结果如下:

本书将展示更多的管道示例。如果将stdoutstderr重定向到文件,通常情况下,如果文件已经存在,它将被删除,或者在写入任何内容之前将创建一个新文件。为了不删除文件,而是追加内容,使用大于号。例如,要创建一个新的输出文件,请执行以下屏幕截图中显示的命令:

现在,要将字符串Hello World附加到输出文件,我们将使用大于号。这不会在我们开始将内容重定向到文件时删除文件的内容。相反,它将在文件末尾附加内容。如前所述,管道是 shell 中最重要的概念之一,与它们一起工作非常有趣。

理解正则表达式

在本节中,我们将介绍正则表达式的奇妙艺术。您将了解它们是什么,以及它们为何如此强大。有许多不同的正则表达式字符可用,在这里我们将介绍最重要的字符。之后,您将学习如何使用grep命令应用正则表达式来查找、提取和过滤文本文件中的有用信息。正则表达式,简称regexps,是一种非常强大的概念,用于使用特殊模式搜索文本,描述搜索项的结构,而不是在这种情况下称为文字文本搜索的常量字符字符串。使用正则表达式可以节省大量时间,不需要重复工作,Linux 系统管理员在日常工作中广泛使用它们。

文件 globbing部分,当我们使用 globbing 字符查找模式以寻址具有一些特殊字符的多个文件名时,我们学习了一个非常相似的概念。正则表达式是一个更强大的工具;它们包含了一组非常广泛的特殊字符,用于完全或部分匹配甚至最复杂的文本片段。在 Linux shell 中,我们使用正则表达式不是为了 shell 扩展或对文件名进行分组,而是为了处理文本文件或文本行的内容,以解析和分析其内容或从中提取文本特征。如前所述,正则表达式是一个非常复杂的主题,我们只能在这里给您一个概述。请注意,有几种风格的正则表达式可用,例如 Perl 正则表达式。在我们的示例中,我们将使用大多数 shell 工具使用的 POSIX、基本和扩展正则表达式。有许多不同的正则表达式字符可用,也称为元字符

由于一些元字符是扩展的 POSIX 字符,我们需要在扩展模式下启动我们的正则表达式处理命令。

一些扩展表达式如下:

  • n用于匹配行的末尾。

  • t 匹配顶部的空格。

  • 插入符^符号匹配行的开头。

  • 美元符号$匹配行的末尾。

  • [x]与 globbing 括号非常相似,您之前已经学过。这描述了在特定位置的括号内匹配的字符类。您还可以在这里定义字符范围。

  • [^x]匹配在括号中未定义的所有字符。

  • 括号用于分组;这将保存括号内的文本以便以后引用。

  • 数字1用于反向引用。这将从括号中提取引用的数字n

  • a|b 表示在此位置允许ab

  • x* 表示匹配在此位置的零个或多个x字符。

  • y+ 表示匹配在此位置的一个或多个y字符的多个出现。

  • 点表示匹配特定位置的任何字符。

还非常重要的是,许多使用正则表达式的工具,如sedawk,期望正则表达式被斜杠包围。另外,脚本语言 Perl 也采用了这种风格。在grep等其他工具中,您不需要使用斜杠表示法。

让我们首先用grep命令来实验一下我们的新正则表达式概念。我们使用egrep命令行工具启动扩展模式的 grep。你也可以用大写的-E选项来运行grep命令,效果是一样的。grep是一个命令,它逐行遍历文本文件或输入流,并尝试将给定的搜索模式参数与每一行进行匹配。如果特定行匹配模式,它将打印出完整的行。这对于各种文本提取非常有用,grep是 Linux 上最重要的命令行工具之一。事实上,我在 shell 中工作的一天中,几乎没有不使用它的。很多时候,grep被用作管道命令工作流的一部分,以减少你想进一步处理的大量输出文本。

首先,正如前面所说,我们将使用 POSIX regex。有很多不同的正则表达式术语,太多了,无法记住,所以每次需要查找语法时,输入man 7 regex。在这个手册中,你会找到关于正则表达式的所有你需要知道的东西。

让我们开始从文件中提取各种信息。我们将首先使用grep命令,而不是使用正则表达式,而是搜索简单的文本字面量,grep root /etc/passwd。这将返回passwd文件中包含单词root的所有行。输出中的任何行都会给我们提供属于root用户的信息。正如你所看到的,grep 会遍历整个文件,并找到包含字符串root的所有行。一个非常有用的grep选项是-i。这可以用于忽略搜索项的大小写敏感性。例如,执行grep -I root /etc/services。这将找到单词root的所有出现,同时忽略大小写。这也将找到单词root的所有其他大小写排列。当使用正则表达式作为grepsedawk等命令的参数时,建议用单引号引用你的元字符。这是因为一些正则表达式字符与 shell 通配符字符相同,比如通配符字符,这是不好的。在任何参数被输入任何命令之前,shell 扩展总是发生的,所以使用正确的命令而没有禁用通配符字符会搜索包含你想要搜索的特定文件中所有文件名的字符串。

相反,总是将你的正则表达式元字符放在单引号中。另外,如果你想在文件中搜索与正则表达式元字符相同的特殊字符,你需要转义该字符,这与我们在文件通配符部分学到的使用反斜杠键类似。以下截图说明了本节开头提到的每个元字符的一个例子:

美元符号匹配行尾,因此这将打印出在 services 文件中以 data 结尾的所有文件。类似地,我们使用插入符^符号来匹配行首。以下命令在这里匹配所有以单词day开头的行:

括号表达式是括在方括号中的字符列表。它通常匹配列表中特定位置的任何单个字符。您还可以使用破折号符号在方括号中定义范围,类似于我们在文件通配符部分中展示的那些。如果括号中的列表以插入符号开头,它匹配不属于此列表其余部分的任何单个字符。普通括号可用于保存其中的匹配的引用。要进行反向引用,我们使用括号表达式的/number,以便正则表达式匹配所有以第一个字母开头的行,例如,egrep 't(ac)1*s' /etc/services。管道符号代表,因此下一个表达式匹配所有包含domaingopher的行。点匹配特定位置的任何字符。加号表示匹配字符之前的零个或多个出现,因此这个正则表达式匹配所有包含at-的行,但不是在行末。星号元字符匹配字符之前的一个或多个出现,因此这里的egrep 'aa+' /etc/services表达式匹配所有包含至少两个aa或更多的行。加号字符匹配字符之前的一个或多个出现,因此这个正则表达式在这里匹配所有的行。

如前所述,点匹配表达式中特定位置的每个字符,因此正则表达式匹配所有包含与表达式中的点数相对应的字符数的行。grep有许多有用的选项,例如,-v反转搜索匹配,这意味着打印出所有不包含搜索模式的行。我经常使用这个选项来删除所有以井号开头的空行和命令行在许多配置文件中的 shell 脚本文件。例如,执行以下截图中显示的命令:

手册包含许多以井号和空行开头的命令行。要过滤掉所有这些不需要的行,可以使用grep -v选项。另一个有用的功能是grep -o选项,它只打印匹配的模式,而不是完整的行。因此,例如,egrep 'netbios-...' /etc/services会打印出完整的行,而-o选项只会打印出模式中的纯 NetBIOS 名称。

使用 sed 进行工作

在本节中,我们将学习关于sed命令,这个强大的流编辑器。我们将为您简要介绍sed的工作原理,并向您展示自动替换文本和文件的替换模式,这是最重要的可用模式之一。接下来,我们将学习关于sed命令。让我们首先检查它的语法:

sed [OPTION] 'pattern rule' FILE  

sed代表流编辑器,这个命令可以在没有任何用户交互的情况下自动编辑文件。它逐行处理输入文件。通常情况下,sed用于 shell 脚本中,将任何命令的输出转换为进一步处理所需的形式。大多数sed的日常用例都遵循类似的模式,最简单的形式是首先使用正则表达式或其他模式来定义要在输入文件或流中更改的行,然后提供一个规则来更改或转换匹配的行。与grep命令类似,使用sed时总是使用单引号,除非您需要在sed表达式中使用环境变量,那么您应该改用双引号。通常情况下,sedstdin读取,内部处理流,并将文本的转换版本输出到stdout。因此,它最好用在pipe命令中,因此它经常是管道的一部分。sed可以用于许多不同的用例。

使用地址范围的一个非常简单的示例是d选项,用于删除,这也有助于您了解sed如何在输入和输出流上进行处理。因此,再次,cat /etc/services | sed '20,50 d'etc/services文件流使用cat管道传输到sedsed逐行处理输入流,在这里,所有不在 20 到 50 行之间的行直接处理到stdout通道,而 20 到 50 行的行完全被抑制。您还可以在d选项中使用正则表达式。请记住,在使用sed时将任何正则表达式放在斜杠中。sed命令会忽略以井号符号开头的所有行,但会将所有其他行打印到stdout。有许多不同的选项和模式可以工作,但这里提及的太多了。

sed的最重要用法肯定是替换模式,它可以用于自动化文件或文本编辑,无需任何用户交互。其一般语法是:sed 's/search_for_text/replace_with_text/' FILENAME。这将在文件 filename 中搜索第一个斜杠之间的模式,该模式可以是正则表达式或文字表达式,并且仅当此模式在文件中的某行中的文本与之匹配时,才会被另一个斜杠之间的文本替换。这仅适用于文件中的第一次出现。如果您需要替换文件中搜索文本的所有出现,必须在斜杠表达式的末尾使用g选项。例如,要替换passwd文件中的单词root为单词King_of_the_Jungle,并且要替换每次出现,执行以下屏幕截图中显示的命令:

如果您正在搜索包含斜杠的任何内容,可以使用不同的模式分隔符来转义常规替换用法,否则您将需要转义要搜索或替换的斜杠字符,这看起来可能非常复杂和无结构。这也可以写成sed 's:XX:YY:g' FILENAME,或者您选择的任何其他字符。例如,如果您想在文件中用双斜杠替换单斜杠,而不是使用sed 's//////g' FILENAME,最好使用sed 's:/://:g' FILENAMEsed 's#/#//#g' FILENAME。在不带任何sed选项的替换模式下,将始终将转换后的文本打印到stdout。有时,直接更改输入文件中的文本是有用的。这可以使用sed -i选项或内联选项来完成。

在以下示例中,我们将处理passwd文件的副本,以向您展示如何进行原地编辑。为此,请执行以下步骤:

  1. /tmp目录中创建passwd文件的副本,如下屏幕截图所示:

  1. 让我们首先显示所有包含单词root的行。

  2. 接下来,在文件中用随机文本替换root一词,仅在stdout上执行以下命令:

sed 's/root/RULER_OF_THE_WORLD/g' /tmp/test-passwd | less  
  1. 现在要进行原地编辑文件,请使用-i选项:
sed -i 's/root/RULER_OF_THE_WORLD/g' /tmp/test-passwd
less /tmp/test-passwd  

文件已经永久更改。在使用此选项时请小心,因为如果您在进行替换之前没有测试过,并且犯了一个错误,您将无法恢复更改。在应用原地编辑之前最好创建原始文件的备份副本,您可以使用sed -i选项来执行此操作,例如,sed -i.bak 's/root/RULER_OF_THE_WORLD/g' /tmp/test-passwd。如果在-i选项后面写入新的扩展名,例如.bak,它将在将正则表达式应用于原始文件之前创建一个带有扩展名bak的备份副本。在使用这些正则表达式进行替换模式时,我们之前向您展示的分组和反向引用功能使替换非常强大,因为这使您真正控制了需要对输入文本进行的更改,例如,passwd文件包含冒号作为字段分隔符,一个冒号用于分隔一个字段。使用 sed 进行反向引用时,我们可以用四个冒号替换一个冒号:

grepsedawk使用的 POSIX 扩展正则表达式还在括号中定义了许多非常有用的特殊字符类,这在模式匹配中非常有用。一般的语法是grep '[:digit:], [:space:], [:blank:]'。数字括号字符类匹配特定位置的所有数字。空格匹配所有空格,空白匹配所有空格,例如Tab空格,空白匹配所有空格。要匹配etc/passwd文件中包含数字的所有行,请使用grep '[[:digit:]]' /etc/passwd。要查看所有特殊字符类的列表,请使用man 7 regex手册。

使用 awk

在本节中,我们将向您展示awk命令的全部内容以及它对我们而言的重要性。我们还将向您展示如何将其用于文本文件操作和处理。awk是另一个非常重要的文本处理和操作工具。它可以作为一个完整的脚本语言来处理文本文件或流。它包含一些非常强大的编程结构,包括变量:if...elsewhiledo whilefor循环;数组;函数;和数学运算。awk也像sed一样逐行处理。awk的一个关键特点,也是与sed的主要区别之一,是它会自动将输入行拆分为字段。但它是如何工作的,为什么它如此有用呢?

awk使您能够创建规则和动作对,对于每个匹配此规则或条件的记录,动作将触发。规则也称为模式,非常强大,并且可以使用扩展正则表达式。动作的语言类似于编程语言 C。使用awk符号范例在输入中查找模式,然后应用某种动作通常将复杂和繁琐的数据操作任务减少到几行代码,甚至一行代码。awk还允许您创建和执行强大的awk脚本文件,以自动化具有挑战性的文本转换任务,但在本节中,我们将仅专注于在命令行上使用awk选项和参数。请注意,由于awk是一个具有许多功能和选项的完整脚本语言,因此我们只能在这里向您展示最重要的用例和示例。

这是任何awk命令的基本结构:awk [pattern] { action }...INPUTFILE。重要的是要注意,动作必须用花括号括起来。这也可以理解为:逐行浏览输入文件,并尝试将模式应用于每一行。如果且仅当模式匹配或规则可以应用于该行并且为真时,花括号之间的动作将被执行。

学习和理解awk工具的最简单方法是在没有任何规则或模式的情况下使用它,并定义一个简单的操作。不给定模式,操作将应用于任何输入行。如前所述,awk完全将每个输入行拆分为字段,因此我们可以在操作参数中直接访问这些字段,使用以下表示法。与往常一样,操作和模式应放入单引号中:

awk '{print $1}' /etc/networks  

这将打印出etc/networks文件的所有行的字段1。正如您现在可能已经知道的那样,awk默认在每个空格位置拆分。您可以使用-f选项来更改字段分隔符。例如,要正确拆分具有冒号作为字段分隔符的passwd文件,您将使用冒号指定字段分隔符-f。这将打印出etc/passwd文件和用户名的第一个字段:awk -F: '{ print $1 }' /etc/passwd。您还可以使用 awk 的printf函数,它会按照您从其他编程语言中了解的方式打印出格式化的文本:awk -F: '{ print "user: %stgroup: %sn", S1, S3 }' /etc/passwd%s将被字段号替换。t表示Tab字符,n表示换行字符。

现在,是时候测试一些模式了。如前所述,如果您定义的模式或规则也可以是扩展的正则表达式,它将应用于每个输入行,只有符合规则的行才会执行操作。

以下命令将打印出所有以小写t开头的行的第一个字段,并且只有这些行,位于etc/services文件中。在这里,我们将其传输到head命令中,以将输出减少到前 10 行:

请记住,在使用awk命令时,将任何正则表达式放入斜杠中。awk 的最大特点之一是模式也可以不仅仅是简单的正则表达式。例如,您还可以在此处使用字符串和数学比较运算符。这将帮助您仅使用几个小表达式回答非常复杂的文本操作问题:

在前面的示例中,awk仅输出etc/passwd文件中组 ID 大于500的用户行。大于号是操作符。还有许多其他可用的操作符,但这里无法一一列举。例如,要匹配正则表达式,请使用awk '$1 ~ /netrjs/ {print $0}' /etc/services。波浪号是正则表达式匹配操作符。要匹配字符串文字,请使用两个等号,例如awk '$1 == "netrjs-4" {print $0}' /etc/services。要获取所有 awk 操作符的列表,请在 man 页面中搜索操作符。此外,awk还有两个特殊模式,称为BEGINEND。与任何其他模式一样,您可以为开始和结束模式定义一个操作,这将在文件的开头或结尾处仅触发一次。我们可以使用这个功能来打印出目录中的总字节数:

这个awk命令的工作方式是:首先它使用一个名为SUM的变量,它充当我们计数数字的容器。+=是一个数学运算符,它将字段号5添加到我们的容器SUM中,因此,此操作计算每行中字段5的单字节数字的总字节数。此外,在每行上,我们打印出整行内容,并且一旦到达文件末尾,将触发结束模式,它将打印出我们的SUM变量的内容,其中包含此目录中的总字节数。正如您刚才看到的,我们可以定义自定义变量来保存我们想要拥有和处理的值。awk 中还有许多预定义的变量名称,其中包含非常有用的信息。例如,NR变量名称包含当前行号。这在以下awk命令中可能很有用:

awk '{printf "Line number: %st%sn", NR, $0}' /etc/passwd  

这将使用NR变量在输出的每一行前添加行号,该变量包含每一行中的当前行号。要获取所有特殊的 awk 内置变量列表,请使用手册并搜索变量。

awk 包含许多非常有用的预定义函数,例如我们已经从操作语句中了解的printprintf函数。要在一个动作块中执行多个函数,可以使用分号。例如,awk 包含许多非常有用的字符串操作函数,例如toupper(argument)函数。awk 中的函数与大多数其他编程语言中的函数一样。您可以使用函数名称调用它,然后在括号中添加参数或参数。例如,我们在 awk 动作中使用printprintf函数。例如,在 awk 中存在一个名为toupper的字符串函数,它将每个字符串参数转换为大写字母。

以下是一个完整的使用toupper函数的awk命令行示例:

这将正常打印出passwd文件中的第一个字段,然后再次打印出所有字母大写的字段。我们的最终示例将向您展示如何在一个动作语句中使用分号作为表达式分隔符执行多个表达式或函数:

此外,您还可以在此处看到,您可以将任何函数的返回值分配给变量名称,然后稍后引用此变量名称,因此,这个示例与之前的示例非常相似,首先打印出大写版本,然后是普通的小写版本,然后是普通的字段值版本。对于所有可用的 awk 函数,我们使用手册并搜索函数、数字函数、字符串函数、时间函数等。

导航 Linux 文件系统

在本节中,您将学习如何导航 Linux 文件系统。您还将了解 Linux 文件系统的结构。如果我们通过执行tree -d -L 1 /命令打印出根目录下顶级目录的文件夹结构,您将看到一系列听起来奇怪的目录名称。这些目录名称在任何 Linux 发行版上都是相同的,并且遵循一种称为文件系统层次结构标准FHS)的标准。Linux 文件系统中的每个标准目录都有特定的目的,用户可以期望在特定位置找到某些文件,这也意味着程序可以预测文件的位置,也意味着任何使用这些系统目录的程序都可以预测文件的位置。以下是这些目录:

  • /斜杠是主要的层次结构根。

  • /bin包含系统所需的基本命令,例如,当系统出现故障时,用户可以在恢复模式下使用,或者例如,当用户启动到恢复模式时需要的可执行文件。

  • /boot包含用于引导的文件,例如内核文件。

  • /dev 包含了系统的设备文件,例如 /dev/null,我们之前使用过。这个目录非常重要,在你担任系统管理员时会经常用到。它包含了你在系统上安装的所有应用程序的系统范围配置文件。

  • /home 包含了用户的主目录,就像我们在本节中学到的那样。

  • /lib 包含了对 /bin/sbin 中的二进制文件至关重要的库,接下来我们会看到。/lib64 包含了 64 位架构的必要的备用格式库。

  • /media 包含了可移动介质的挂载点,比如 CD-ROM。

  • /mnt 包含了临时挂载的文件系统。

  • /opt 包含了可选的应用软件包。

  • /proc 包含了提供进程和内核信息的虚拟文件系统,例如,当前会话的所有环境变量都存储在这里。

  • /root 包含了 root 用户的主目录。root 用户的主目录不在 /home 中。

  • /run 包含了运行时变量数据;这是关于系统自上次启动以来运行情况的信息。

  • /sbin 包含了必要的系统二进制文件。

  • /srv 包含了系统应该提供的所有数据,例如,用于网页服务器的数据和脚本,或者在系统上作为服务运行的 FTP 服务器提供的数据。

  • /sys 包含了连接到系统的设备的信息。

  • /tmp 包含了临时文件。每个用户都对这个目录有完全访问权限。

  • /usr 包含了大部分用户实用程序和应用程序,例如,用户安装的所有应用程序都在这里。它也被称为次要层次结构,用于只读用户数据;因为它有与根目录相似的结构,顶级目录。例如,你也有一个 /usr/bin 目录,一个 /usr/lib 目录,一个 /usr/sbin 目录,等等。

  • /var 目录用于系统正常运行期间预计会不断变化的所有文件,例如日志文件、溢出文件和临时电子邮件文件。

首先,让我们介绍 Linux 主目录的概念。Linux 系统中的每个用户都有自己在文件系统中的私人空间,他们可以在其中管理自己的数据,并且拥有对所有内容的完全访问权限,例如,创建目录或新文件,删除东西,或更改权限。出于安全原因,Linux 文件系统中的大多数地方,除了一些例外,例如系统的 /tmp 目录,都在某种程度上受到限制,通常登录的用户没有对其的完全访问权限,只有 root 用户对所有内容有完全访问权限。每个登录的用户都有一个当前目录的属性,这是你当前所在的目录。当用户登录到 Linux 系统时,默认情况下,他们特定的主目录将被设置为当前目录,因此他们将从这个目录开始。

要显示当前目录的名称,也就是你当前所在的位置,输入pwd,然后按Enter键。pwd 代表打印工作目录。这是一个非常有用的命令,因为当你浏览目录时,很容易迷失方向。目录是一种用于组织数据的概念。通常用于对属于同一项目或相同类型的所有文件进行分类,例如所有配置文件。正如你所看到的,pwd命令的输出包含一个包含斜杠符号的字符串,用于分隔目录名称,这也被称为目录分隔符号。最左边的斜杠有一个特殊的名称,也被称为根目录。当前目录的最后一个目录名称也可以在 shell 提示符中看到。在 Linux 文件系统中,每个目录都可以包含文件,并且可以包含进一步的子目录,然后称为子目录。这些子目录也可以包括文件和文件夹等。包含子目录的目录也被称为父目录,而子目录被称为子目录。在我们的例子中,主目录是olip目录的父目录,也被称为子目录。这种类型的文件和文件夹可以使用类似树状结构的方式进行可视化,也可以称为分层文件系统,因为该结构中的每个目录都有特定的位置,有些在层次结构中更高,而其他的更低。最高的目录是/目录,或root目录。我们需要记住可视化这种分层树结构。我们可以使用tree命令,但需要安装,因为它不在标准安装中。要安装它,使用你在安装过程中设置的root密码。

安装完成后,可以使用tree命令来首次概览系统。在顶层,我们有/目录,这是树中最高的目录。直接在其下有许多系统目录。当我们定制tree命令以显示树中两级目录时,可以看到主目录在树中的位置,以及如何从根目录到达它,根目录是所有其他目录的父目录。现在,要在主目录中创建一个新目录,可以使用mkdir命令。mkdir命令以要创建的文件夹的名称作为参数。要删除一个空目录,使用mrdir命令。要创建一个新的空文件,使用touch命令。要删除一个文件,使用rm命令。

现在,让我们重新创建文件夹和文件名。要切换到一个目录,可以使用cd命令,它代表改变目录。改变目录命令将当前目录更改为cd命令的参数所指定的新目录。使用pwd再次测试这一点。以下截图说明了这一点:

在 Linux 中,当我们说要进入一个目录时,我们真正的意思是通过使用cd命令将另一个目录设为当前目录。如前所述,每个目录都包含两个特殊的快捷链接,你无法更改也无法删除,即...,它是我们当前所在目录的名称。每个目录都包含名称为..的链接,它是我们当前所在目录的唯一父目录的名称。此外,每个子目录都只包含一个父目录,而一个父目录可以包含多个子目录。这些点对于快速浏览目录非常有用。要返回到上一个目录,也就是我们的主目录,我们可以使用..符号。要在子目录中创建子目录,可以使用以下方法:

要查看我们刚刚创建的文件夹结构,我们可以再次使用pwd命令。要上升一个目录级别,我们可以使用cd..。要返回,使用cd FolderD。现在,要上升两个目录级别,你可以使用目录分隔符斜杠符号-cd ../ ../。要返回我们子目录结构的两个级别,我们也可以使用目录分隔符斜杠符号。在遍历目录时,总是有很多方法可以做到这一点。要快速返回主目录,我们可以使用几种不同的方法。首先要返回主目录,你可以使用一些快捷方式。正如我们之前提到的,波浪号符号代表主目录,所以我们可以轻松返回主目录cd ~。波浪号符号可以从任何地方使用,所以你可以从任何目录返回到你的主目录。另一个非常有用的快捷方式是cd -,它可以让你在当前目录和之前所在的目录之间切换。

甚至存在一种更短的方法,可以在任何位置只使用cd命令而不带任何参数就返回到你的主目录。从任何位置进入你的主目录的另一种方法是直接使用pwd命令输出的路径。为了删除包含子目录或文件的目录结构,你不能使用rmdir命令。相反,为了删除包含文件和目录的目录子树,我们需要使用rm -rf选项,但请谨慎使用,因为这将无需询问地删除所有内容,这是完全不可逆的。

为了重新创建相同的子目录结构,我们可以使用目录分隔符符号和mkdir -p选项,这样我们可以以更简单的形式展示。到目前为止,我们对文件和文件夹的所有操作和动作,比如lsmkdirmrdir,都是相对于当前目录的,这意味着如何进入所选目录或文件的描述总是相对于当前目录的。例如,我们使用命令来处理位于当前目录内的文件和目录。要引用当前目录之外的文件和文件夹,我们使用..和斜杠目录分隔符。

如果我们想要在同一目录中处理文件和目录,我们只需要资源的名称。如果我们想要访问当前目录之外的资源,我们可以使用目录分隔符和..符号来访问正确的文件或目录。现在,让我们再次执行pwd命令。pwd命令的输出被称为绝对完整路径。绝对路径很容易从前导斜杠识别出来,这被称为根目录,正如你现在所知道的。斜杠符号意味着你从顶层目录,或根目录开始,然后继续向下。绝对路径实际上是一个沿着层次结构的路径。路径名指定并描述了如何遍历或导航文件系统中的分层目录名称,以达到从最高根目录开始的某个目标对象的目的。完整路径始终包含了如何从根目录到文件系统中的任何目的地的完整信息。换句话说,要进入当前目录,也就是/home/olip/FolderA,你必须从/root目录遍历到home目录,然后到olip目录,最后到FolderA目录。要以类似树状结构的方式可视化这一点,使用带有子目录L 3tree命令:

重要的是要记住,绝对路径可以从任何地方起作用。相对路径没有前导斜杠。例如,使用相对路径切换到FolderA,始终取决于您当前在文件系统中的位置。因此,cd FolderA仅在当前位置起作用。如果您在其他地方重新执行命令,它将无效。在使用任何与文件或目录相关的 Linux 命令时,您始终可以选择使用相对于当前目录的本地路径,或者使用相对于根目录的完整绝对路径。通常情况下,相对路径更快且直接切换到要直接处理的文件目录也更方便。但绝对路径对于脚本或需要从任何目录起作用的命令很重要。

总结

在本章中,我们从介绍命令行、文件通配符和引用命令开始。我们通过使用 shell、标准流和正则表达式来进行实际执行。我们还涵盖了sedawk和 Linux 文件系统的功能。

在下一章中,我们将涵盖与文件相关的概念。

第三章:Linux 文件系统

在上一章中,我们通过浏览文件系统向您介绍了 Linux 文件和文件夹。在本章中,我们将学习如何处理、查找和更改文件的权限和访问权限以及读取和编辑文件。我们将扩展我们对这个主题的知识,定义文件系统是什么,并向您展示处理文件的重要命令,如复制和移动。

我们将为您介绍以下概念:

  • 了解文件系统

  • 使用文件链接

  • 搜索文件

  • 与用户和组一起工作

  • 使用文件权限

  • 处理文本文件

  • 使用 VIM 文本编辑器

了解文件系统

文件系统不仅是向 Linux 用户公开的文件和文件夹树,还是访问和保存数据以及保持一切一致的结构和管理。如前所述,您经常听到 Linux 中的一切都是文件,这是真的。这意味着 Linux 中的许多不同事物都被抽象为文件。例如,目录是一个文件,硬件设备由特殊的系统文件表示,或者,有用的东西,如随机数生成器,也是一个文件。

让我们快速回顾和总结我们在过去两章中已经了解的有关处理文件的知识。ls列出并显示文件,touch创建文件,文件区分大小写,.文件是隐藏文件,并且被排除在正常命令执行之外,例如ls命令,也被排除在使用文件通配符进行 shell 扩展时。接下来,您还应该知道mkdir创建目录,rmdir删除空目录,rm删除文件,mkdir -p创建完整的子目录结构。rmdir不能应用于非空目录;在我们的示例中,它包含子文件夹。rm -rf删除带有所有子目录的目录,但要小心处理。当您使用rm -rf选项删除整个目录结构时,r选项代表递归,f代表强制。递归选项是您在处理对整个子目录树执行某些操作的命令时经常遇到的重要选项。

现在,让我们学习一些其他重要的基于文件的命令。如果您需要复制文件,可以使用cp命令。我们已经在上一章中看到了cp的一般用法。如果您查看cp命令的手册页,有三种不同的用法格式:cp [option]... [-T] SOURCE DESTcp [option]... SOURCE... DIRECTORYcp [option]... -t DIRECTORY SOURCE...。重要的是要记住,可以有多个源目录,但只能有一个目标目录。您必须记住这一点。要将单个文件复制到一个目标文件,可以使用第一种用法形式。要将多个文件复制到一个目标文件夹,可以使用第二种用法形式。第三种用法形式类似于第二种用法形式,但混合了源和目录参数。

例如,要创建具有不同文件名的文件副本,请使用cp firstfile secondfile;您也可以使用本地路径名执行相同操作。正如我们从手册中学到的,您还可以将文件复制到目录,保留原始文件名。如手册中所示,您还可以对多个源文件执行此操作。请注意,您不能直接使用cp命令复制完整的目录。为了这样做,您需要提供-R选项:

如您所见,完整的olip主目录已被复制到/tmp目录中,包括所有子目录和文件。请记住,cp -R选项再次代表递归。要在命令行上移动文件和文件夹,可以使用移动命令,它隐式地复制并删除源文件。移动命令mv通常用于重命名文件和文件夹。请注意,您不仅可以移动或重命名文件,还可以移动或重命名文件夹。

现在,再次看看ls -l选项,例如,在/etc目录中,你会注意到一些东西。你将看到很多有用的信息。在-l列表中的第一个字符是d-l,这代表文件的类型。d代表目录,-是普通文件,l是链接。ls -l输出中的第一个字符也被称为文件类型标志。除了显示的d-l标志外,还有许多其他可用的文件类型。要获取所有可用文件类型标志的完整列表,请使用man find,搜索type,然后搜索type c。你将得到 Linux 中所有可用文件类型标志的完整列表:

此外,在权限列的相邻位置还提供了另一个非常有用的信息。所呈现的数字是文件包含的链接数:

文件链接告诉我们任何给定文件或目录上存在多少引用。每个普通文件默认有一个链接,每个目录有两个链接。有硬链接和软链接,我们将很快讨论。

处理文件链接

在本节中,我们将看看 Linux 文件链接是什么,以及如何使用它们。正如你可能已经知道的那样,文件存储在硬盘上。在 Linux 文件系统中,文件的文件名和数据是两个分开的概念,它们不存储在一起。下图显示了一个一般的结构:

将文件名连接到实际数据是由文件系统使用表或数据库数据结构来管理的,这被称为标题分配表。在 Linux 文件系统中,Inode 是实际的入口点或特定文件数据在硬盘上的起始点。简单来说,我们可以说 Inode 代表文件的实际数据。文件系统管理现在负责确保每个普通文件在创建时在其分配表中有一个链接条目,以连接实际文件名到 Inode 或硬盘上的数据。这样的链接也被称为硬链接。原始文件名到 Inode 的关系也使用硬链接进行连接,这就是为什么在上一节中ls -l命令在权限列的相邻位置给我们大多数文件的数字1。现在,Linux 文件系统的很酷的一点是你可以创建额外的硬链接到现有的 Inode,这就像为一个文件创建替代名称一样。

硬链接的一个缺点是你无法区分硬链接和原始文件名或 Inode。这可能会导致问题和副作用,因为如果你更改原始文件的内容,硬链接的内容也会被更改。硬链接的另一个限制是你只能为应该在同一分区上的 Inode 定义它们。此外,你不能在目录上创建硬链接。你只能在普通文件上创建它们。为了解决这些硬链接的限制,你可以使用软链接,也称为符号链接。这是你作为 Linux 系统管理员在日常工作中几乎总是会使用的链接类型。硬链接也有它们特殊的用例,例如用于创建文件的备份,但在 Linux 用户中很少使用。

符号链接是对文件名而不是 Inode 的链接。符号链接也没有必须在同一分区或硬盘上的原始文件的限制。您还可以在目录上创建符号链接。主要缺点是,如果删除或移动原始文件,您将得到一个中断的符号链接,而不会有进一步的警告,这也可能会产生一些不良副作用。符号链接的主要用途和功能是引用 Linux 文件系统中的配置文件或动态库版本。使用链接可以节省大量磁盘空间,因为不必复制实际数据,并且对于快速测试诸如服务的备用配置文件等内容非常有效。

文件链接由ln命令管理。基本语法是ln [OPTION],然后是要在其上创建链接的文件名,最后是链接名。要在您的home目录中创建一个名为fileX的文件的硬链接,请使用以下代码:

如您所见,无法区分附加的硬链接和原始链接。您还可以在同一文件上创建多个链接。要删除硬链接,请使用rm命令。每个文件系统或者我们可以简单地说文件都有最大数量的 Inodes,您可以使用df -i来显示。如果使用mount命令,您将看到用户的tmp文件系统位于与home目录不同的分区上,而home目录又位于根分区上,如下面的屏幕截图所示:

因此,下一个命令ln ~/folderABC ~/folderABC_link将失败,因为不允许在分区之间创建硬链接。此外,您不能在目录上创建硬链接,更改文件内容的原始位置也将更改硬链接的文件内容。这可能会产生一些不良副作用。要创建符号链接,请使用ln -s选项:

如您所见,很容易显示文件是否为符号链接,标有箭头。要在另一个目录中创建文件的符号链接,保留原始文件的名称,您可以使用ln -s /etc/passwd。这在当前目录下创建了/etc/passwd文件的符号链接,名称相同为passwd。要删除符号链接,请使用rm命令;原始文件不会被触及。您还可以在目录上创建符号链接。如果删除符号链接指向的原始文件,即fileX,这里符号链接将中断。这可能会变得棘手,这在这里用蓝色表示:

搜索文件

在本节中,我们将学习如何在 Linux 中搜索文件。man find命令可以根据多种条件查找文件,正如其名称所示。但更重要的是,您甚至可以在程序执行期间对每个搜索结果应用操作,这是一个非常有用的功能。Find 可以采用一些选项来更改其默认行为,例如如何在程序执行期间处理符号链接。前几个参数是要在其中开始搜索的目录或起始点的列表,所有其他参数都是搜索表达式或条件,用于在搜索中查找。讨论搜索表达式的内容很重要。搜索表达式通常是一个测试和一个操作。测试通常由逻辑运算符分隔。如果没有给出运算符,则假定为结束运算符。如果表达式中没有用户指定的操作,则将对搜索结果中的所有文件执行print操作。

在开始使用man find命令之前,了解man find命令如何处理搜索结果非常重要。对于搜索路径列表中的每个文件,所有表达式都会从左到右进行评估。默认情况下,只有当所有表达式都正确时,man find命令才会将文件标记为命中。如果需要,您还可以更改此逻辑结束行为,使用OR表达式,我们将在其中一个示例中稍后看到。man find命令允许您使用广泛的非常有用的文件测试表达式创建非常复杂的搜索查询。如果在man find命令的手册页中搜索tests,您将获得所有可用测试运算符的完整列表。例如,您可以搜索在过去的特定时间修改或访问的文件,或者具有特定大小的文件。如前所述,默认操作是对每个文件匹配执行print操作。另一个非常有用的操作是exec表达式,它允许您对每个文件匹配执行特定命令。man find命令是一个非常复杂的命令,我们无法在这里展示所有内容。因此,在本节的其余部分中,我们将展示一些非常有用的用例。您可以使用find命令而不带任何选项或参数。这与编写相同,因为没有任何选项和参数,搜索路径是当前目录,而默认操作是print操作。此命令遍历您当前的目录,并打印出所有文件和目录,包括所有子目录和子目录下的文件,进行递归处理。这是因为您没有提供任何测试表达式,因此它将匹配当前目录中的任何文件或目录,并对其应用print操作。如前所述,find命令如此强大的原因在于其巨大的不同测试表达式列表,可以根据各种有用的条件定位文件。这样的文件搜索测试可以是任何想象得到的,例如时间戳、用户权限、用户、组、文件类型、日期、大小或任何其他可能的搜索条件。

在以下示例中,我们将使用安装过程中设置的根用户帐户,因为在此示例中,我们需要在系统目录中进行大量搜索,这需要特殊权限。要仅在/etc目录中搜索文件而不是目录,以查找文件名为logrotate.conf,请使用以下命令:

find /etc -type f -name logrotate.conf  

如果找到文件,您将不会遇到任何错误。此命令在后台执行的操作是遍历/etc目录,并选择其中包含的所有文件和子目录,并逐个递归处理它们。然后,对于每个文件,它都会检查文件是否为实际文件,以及名称是否等于文件名。您还可以使用多个目录作为搜索起点,以及使用-type d仅搜索目录,这将打印以/etc/var目录开头并以字母y开头的所有子目录名称:

在这里,名称表达式采用正常的 POSIX 5 通配符,而不是正则表达式。如果您想要使用正则表达式进行文件搜索,请改用-regex表达式。请注意,如果您使用-iname表达式,它将进行不区分大小写的搜索。您还可以按文件大小作为条件搜索文件:

find / -type f -size +4M -name 'l*'搜索以名称l开头的大小等于或大于 4 MB 的所有文件,只有文件而不是以root目录开头的目录,这意味着它将递归搜索整个文件系统树。正如您所看到的,只有两个文件符合所有这些条件。顺便说一句,+表示大于或等于,如果使用-符号,则表示小于。您还可以搜索特定的文件权限。文件权限通常在后面的部分中讨论。要获取具有读、写、执行权限的所有非常危险目录的列表,以便在整个文件系统中搜索,我们使用以下命令:

find / -type d -perm 777  

请注意,如果用户没有为find命令本身提供任何操作,则假定默认的print操作,因此该命令将将每个匹配的文件打印到stdout命令行。我们可以使用-exec操作表达式进行更改,该表达式将在-exec表达式之后为每个匹配的文件应用一个命令:

find / -type d -perm 777 chmod 755 {} ;  

在我们的示例中,chmod 755命令将应用于每个匹配的文件,使用占位符{},表示匹配。此处的find命令将搜索所有具有非常危险的文件权限777的文件,并将其更改回更适度的权限755。因此,如果我们再次搜索危险的权限,结果将为空。为什么我们必须转义分号?这是因为在 Bash shell 中,分号通常用于分隔命令,因此我们必须在这里禁用其特殊含义。到目前为止所示的所有示例中,单个find命令的所有测试和表达式都必须为 true,文件才能被视为匹配。

例如,命令find / -type f -size +4M -name 'l*'只有文件匹配并打印出来,如果它们是文件类型并且大小为4 MB 或更大,并且名称以l开头。所有这三个测试表达式都必须为true,并通过逻辑与连接。默认情况下,逻辑 AND 运算符连接所有测试表达式,这意味着只有当所有测试表达式都为 true 时,文件才能匹配为命中。您可以轻松地将逻辑 AND 更改为逻辑 OR,使用-or表达式,例如:

find / -type f -name p*.conf -or -name 'p*.d'  

这将匹配所有以p开头并在/etc目录中具有conf.d扩展名的文件,并且类型为文件。还有一些基于文件时间的非常有用的测试表达式。例如,find /var -mtime 10| head 将输出所有在最近三天内修改的文件,仅在最后三天或更长时间之前输出前10个命中。使用基于时间的测试表达式非常有用,并且通常在您作为系统管理员的日常工作中需要。例如,如果您需要删除服务器上运行的 Web 应用程序的用户上传的所有文件,这些文件的年龄超过30天,可以执行以下操作:

find /var/www/webapp-uploads -mtime +30 -exec rm {} ;  

此命令也可以轻松地放入每天运行的脚本中,例如在Cron作业中,以自动删除所有早于30天的文件,因此您不必再手动处理此问题。要搜索整个文件系统中以lr开头且大小在 1 到 4 MB 之间的所有文件,请使用:

find / -type f -size +1M -size -4M -name 'l*'  

您还可以使用locate命令快速搜索文件,而不是使用 find。您首先需要使用packagelocate进行安装。locate命令不会在文件系统中进行实时搜索,而是使用特定时间点的文件系统快照。该数据库每天在特定时间点进行更新,但您也可以使用以下方法自行重新生成快照数据库:

updatedb  

现在,如果您使用locate命令,它将搜索您刚刚生成的数据库,查找所有与名称logrotate匹配的文件。这只会搜索文字。如果要使用正则表达式,可以使用--regex选项。

由于我们正在搜索一个数据库,这通常比使用find命令进行实时搜索更快,但请记住这不是当前文件系统的实时状态。因此,当搜索比搜索数据库更新的文件时,可能会遇到问题。

与用户和组一起工作

在这一部分,我们将学习如何创建和删除用户和组,以及如何将组添加到用户中。此外,我们将了解 Linux 如何在内部存储用户信息和密码,以及如何以编程方式检索用户信息。最后,我们将学习如何在登录的同时替代用户账户。Linux 是一个多用户系统,这意味着多个用户可以同时使用系统。因此,需要一个系统来保证对 Linux 对象(如文件)的共同访问,使用过度保护的措施。例如,一个用户创建的所有文件不应该被另一个用户删除。每个 Linux 用户都由唯一的用户 ID 定义和识别,因为人类更容易使用名称而不是数字。每个用户 ID 还有一个文字用户名,但在 Linux 内部处理 Linux 对象(如文件)的控制时,使用的是用户 ID(UID)号码。有两种类型的用户账户,需要密码进行身份验证的登录用户和非登录用户,后者对于将用户 ID 附加到正在运行的程序或进程非常有用,我们稍后会看到。此外,每个 Linux 系统都有一个特殊的账户,即根用户账户或管理员账户,在安装过程中我们会为其设置密码。每个系统上的这个账户都可以访问并拥有所有对象,如 Linux 系统中的文件,并且可以对系统进行任何操作。如果在 Linux 中我们使用用户名来控制访问文件,那将非常限制和耗时来授予或撤销权限。因此,Linux 还有组的概念来进行访问控制。使用组可以通过将权限分配给一个组来大大简化权限管理,而不是分配给单个用户。将权限分配给一个组会将对资源的相同访问权限分配给该组的所有成员。Linux 组也由一个组 ID 表示,这是一个数字,但也可以通过其名称(组名)来引用。Linux 中的每个用户都有一个 UID,但可以属于多个组或组 ID。一个组是主要组,在该用户创建新文件时将使用该组。

让我们从为我们的测试创建一些新用户帐户开始。只有root可以做到这一点。所以首先,让我们以 root 身份登录。useradd命令添加一个以参数给定的用户名的新用户。此命令在系统中创建一个新用户,并创建相应的主目录。为了使我们的新登录帐户可用,我们还需要设置密码;我们可以使用passwd命令来做到这一点。您也可以使用此命令来更改自己的密码。要为其他用户设置或更改密码,我们输入passwd 用户名-这只能使用 root 用户帐户来完成。要删除用户,请使用userdel命令。默认情况下,userdel命令不会删除用户的home目录,因此您必须自己执行此操作。要删除用户,最好使用userdel -r标志,它不仅会删除用户,还会删除相关的home目录和邮箱。让我们重新创建用户。让我们退出 root 用户。您可以使用su命令或替代用户命令在登录时切换用户。当您调用su命令而没有任何参数时,假定已切换到 root 用户。要将替代用户切换到不同的用户,您使用用户名作为第一个参数。您可以使用whoami命令重新检查谁已登录。使用su命令将保留执行su命令的用户的原始环境,并且不会切换到替代用户帐户的 home 用户。

现在,让我们退出替代用户,并切换到另一个用户。使用su命令并带有破折号作为参数,我们创建一个更类似登录 shell 的环境,这意味着它的行为更像一个真正登录到 shell 的用户。通过执行pwd命令,您可以看到home目录已经更改为替代用户的home目录。现在再次退出替代用户。您还可以使用su -c标志直接使用另一个用户帐户执行单个命令;su用户名-c。如果您想要快速使用不同的用户帐户启动脚本或命令,而不完全切换用户,这将非常有用。

只有 root 用户被允许使用su命令在不提供密码的情况下替代用户。任何其他想要使用su命令的用户都需要知道替代用户的密码。

useraddpasswd命令正在对etc/passwdetc/shadow文件进行更改,这些文件是整个 Linux 系统中存储身份验证和用户信息的最重要的文件。passwd文件存储了系统中所有已知的用户帐户列表,所有登录用户和所有系统用户。登录用户通常是可以使用密码进行身份验证登录到 shell(如 Bash shell)的典型物理人员。系统用户通常无法登录到 shell,并且通常与系统服务和进程相关联。

例如,要获取 Linux 系统中所有可用用户名的列表,请使用:

awk -F: '{print $1}' /etc/passwd  

passwd文件存储了许多有用的信息,例如用户的home目录、默认 shell 或用户 ID 号。请参阅passwd命令的手册页以获取更多信息。/etc/shadow文件以加密格式包含所有用户的密码信息。您需要 root 用户帐户才能查看此文件。要创建新组,请使用groupadd命令,要删除组,请使用groupdel命令。groupaddgroupdel命令在内部使用/etc/group文件。此文件显示系统中所有可用的组以及与这些组关联的所有用户 ID。除了读取/etc/group/etc/passwd文件以获取有关用户的信息外,还可以使用id命令。这将告诉您用户 ID、组 ID 以及用户拥有的所有关联组。要将现有组添加到用户,可以使用usermod -G命令。-G会覆盖用户拥有的所有次要组,保留主要组不变。您还可以定义要添加到用户名的逗号分隔的组名称列表。重要的是要记住,-G始终会覆盖用户拥有的现有组名称。

现在,让我们检查权限字符串,它指定了文件所有者、组所有者或任何其他用户根据其身份允许或不允许做什么:

-l在第三列中输出文件所有者。在这里,它是root,对于这个文件是olip。在第四列中,ls -l输出文件的组所有者。同样,在这里是root,在这里是olip。您已经知道ls -l输出中的第一个字符是文件类型。现在,ld之后的九位定义了文件的权限字符串。权限字符串的前三个字符定义了文件所有者的权限。权限字符串的第二个三位定义了组所有者的权限。权限字符串中的最后三位定义了所有其他用户的权限。在这个例子中,folderABC的文件所有者是olip,组所有者是olip。此外,文件所有者olip对目录有完全权限,组所有者olip对目录有完全权限,所有其他用户对此目录有读取和执行权限。

执行以下步骤:

  1. 首先,让我们创建一个目录。我们将放入一些文件以便处理,然后切换到该目录:

  1. 现在,让我们创建一些文件来玩耍。

  2. 让我们看看我们刚刚创建的文件的文件权限。

  3. 如前面的屏幕截图所示,root 用户已创建了所有这些文件。因此,所有文件的文件所有权和组所有权都是root

  4. 现在,要更改文件所有权和组所有权信息,您可以使用chownchgrp命令。

  5. 您还可以使用不同的表示法使用chown命令,而不是使用chgrp命令。

  6. 例如,要更改文件的组,您可以修改文件所有者和组所有权。

  7. 让我们也创建一些子文件夹来测试目录权限。

  8. 也将一些文件放入这些文件夹中。

  9. 接下来,让我们使用一些非常危险的test目录,以便在我们的测试期间每个人都能正确地使用文件。这不是为了生产。

  10. 接下来,为我们的文件创建一些常见权限。

  11. 创建一些不寻常的权限,还为目录测试创建一些权限。

  12. 更改一些文件的所有权权限和目录的组所有权权限。

现在,让我们使用我们的新文件并更改权限。让我们首先查看ls -l命令的输出:

文件一是一个对任何人都有完全读、写和执行权限的文件。例如,系统中已知的任何用户都可以修改这个文件。下一个文件是在 CentOS 7 机器上创建时每个文件都会得到的标准权限。文件所有者有读和写权限,而组和所有其他用户只有读权限。让我们看看如果不同的用户想要修改文件会发生什么:

在这里我们可以看到一些有趣的事情。root用户对任何文件都有读、写和执行权限,尽管在root用户的权限字符串中已经设置了。Peter是文件所有者,所以他可以写入这个文件。Paul既不是文件所有者也不是组所有者,所以他根本没有写权限。下一个文件有一个经常用于机密文件(如密码文件)的权限。通常这是为了运行文件系统用户帐户的服务来保护其他人不读取凭据:

在这个例子中,只有root用户有能力读取文件,其他人都没有。下一个文件有一个常见的权限设置,如果不仅文件所有者,而且文件所属组的成员也应该对文件拥有完全控制权。正如你所看到的,PeterPaul无法写入这个文件,因为他们既不是文件所有者也不是组所有者。为了改变这一点,让我们把Peter添加到拥有这个文件的组中,然后再次测试:

现在,file5有一些不寻常的权限,这是有效的。file5是一个脚本文件,打印一些东西。正如你所看到的,只有root用户可以执行该文件。要让Peter执行该脚本,将与之关联的组之一添加到文件中。这仍然不起作用,因为现在Peter可以执行这个文件,但不能读取它。为了改变这一点,还要为文件的组所有权添加读权限。现在,Peter终于能够运行脚本了:

最后,一个常见的误解是,要删除一个文件,你需要正确设置用户的文件写权限标志,但这是不正确的,正如我们所看到的。为什么Peter不能删除这个文件,即使我们已经给了每个人完全的权限?这是因为文件的删除完全取决于要删除的文件所在目录的写权限,而不是任何文件权限。以下截图显示了用户Peter被拒绝删除文件的权限:

最后,让我们讨论目录权限。以下截图是一个例子:

folderA对文件所有者有读权限,所以只有他能够看到文件夹里面的东西,但不能进入该目录。folderB只对组所有者有读权限,这意味着只有projectA组的成员才能进入这个目录,但Peter除了使用cd命令进入这个文件夹之外什么都不能做:

为了列出这个目录中的文件,让我们将读权限授予组所有者:

正如我们之前学到的,我们需要在目录上启用“写”标志,以便在其中创建或删除新文件。但为什么这里不起作用呢?这是因为我们还需要在目录上启用执行权限,这是有道理的,因为为了在目录中创建或删除文件,我们需要访问该目录。如果我们想要更改很多文件的权限,例如整个子目录树,我们可以怎么做?使用ls -lR标志,我们可以列出所有包括的子目录和文件。现在,要更改子目录中所有文件的权限字符串,可以使用chmod -R标志:

ls -lRchmod 775 -R ../test_files  

一如既往,要小心递归标志,因为你很容易会将整个文件系统的文件权限更改为不安全的不可逆转的权限。

处理文件权限

在本节中,我们将学习 Linux 中文件访问控制的概念。我们还将学习和理解如何读取文件权限。最后,我们将学习如何更改文件所有权以及文件权限,并向您展示实际的文件权限示例。如果使用ls -l打印文件的详细信息,您将看到一系列不同的重要文件属性,我们需要学习以便理解文件权限。典型的ls -l输出看起来像-lrwxr-xr-x olip administrator my-awsome-file.txt. 系统中的每个文件都与一个用户名关联,也称为文件所有者。

每个文件还与一个组名关联,也称为组所有者。文件的所有权只能由 root 用户更改。文件所有权也可以由文件所有者更改。当用户创建新文件或目录时,文件的所有权将设置为创建文件的用户 UID。我们已经知道用户可以属于多个组,但需要设置一个作为主要组。这就是为什么每个新创建的用户都有一个与用户名相同的组。现在,每个想要访问文件的 Linux 用户都可以被归类为这些组中的一个。如果用户 ID 与要访问的文件的所有者 ID 匹配,用户就是文件所有者。如果用户与文件想要访问的组所有者匹配,用户就是组所有者。如果用户既不是文件所有者也不是组所有者,他就属于其他用户类别。这三个权限类别也称为权限组。最后,所有这些权限组,文件所有者、组所有者和其他组,每个都有三种权限类型,读、写和执行。这些权限类型管理着属于这些组之一的用户可以或不能对文件执行的实际操作。

现在,因为我们正在处理每个文件的大量不同信息,用户所有者、组所有者、权限类别和权限类型,一些 Linux 命令(如ls命令)使用了一种非常紧凑的形式来查看,使用了 9 位来完全映射所有权限组的所有权限。这 9 位信息也称为权限字符串。如果授予了读/写/执行权限,或者rwx标志被放置在字符串中的固定位置。如果权限被撤销,可以在字符串的特定位置找到破折号符号。在权限字符串中,从左到右,前三位是文件所有者的读/写/执行权限。接下来的三位是组所有者的,最后的三位是所有其他用户的。9 位权限字符串是一种非常紧凑的表示法,以适应屏幕,并来自计算机硬件中空间和内存昂贵的时代。对权限类型或 9 位权限字符串的更改只能由 root 用户设置或移除。文件和目录上的读、写和执行权限的定义是不同的。

首先讨论文件上下文中的读取、写入和执行的含义。如果文件上设置了r或读取标志,则相应的权限类别,文件所有者、组所有者或其他用户可以打开文件并读取其内容。w或写入标志用于修改或截断现有文件,但需要知道的一个常见误解和重要事实是,写入标志不允许创建新文件或删除现有文件。这不是文件的属性,而是父目录的属性,我们很快就会看到。x或执行标志允许执行文件。这对于在命令行上运行脚本文件或命令非常重要。

为了执行文件以将其作为脚本或命令运行,需要同时设置读取标志,因为 shell 需要读取文件的内容以执行其指令。在目录上下文中,读取、写入和执行权限的含义与在文件上工作时完全不同,这是每个 Linux 用户都必须意识到的。

首先从目录的x或执行权限开始,因为这是文件夹最重要的权限。在目录上下文中,x或执行权限的含义与文件上下文中完全不同。如果文件夹上设置了执行标志,这意味着相应的用户组或其他用户被允许进入该目录或路径,例如,使用cd命令。但x标志不仅对cd命令很重要,如果您需要使用写入标志重命名、删除或创建新文件,这也是必需的。在这里,执行标志也是必需的。作为一个经验法则,如果您需要在文件夹上设置一些标准权限,永远不要错过所需工作的权限组的执行权限,否则您将遇到问题,因为如果要执行某些操作,您总是需要进入目录。r或读取是读取目录内容的权限,例如,使用ls命令。

w或写入标志在目录中创建新文件或删除现有文件。正如我们之前所看到的,删除或创建新文件不是文件权限的属性,而是始终是文件所在目录权限的属性,因此如果要能够在其中创建或删除文件,则必须设置写入标志。为了使用写入标志创建、删除或移除文件,我们还需要为诸如touchrm的命令设置执行标志,因为它们需要访问目录以执行操作。

现在,每个想要执行操作的实际用户,即读取、写入或执行文件或目录的用户,都将由操作系统检查,检查的依据是尝试访问是否合法。这是一个分层过程。首先进行的检查是用户 ID 是否与文件的用户所有者匹配。如果不匹配,将检查用户的所有组 ID 是否与文件的组所有权匹配。如果没有用户组匹配,将使用其他用户。现在,系统中的每个用户都匹配这三个权限类别中的一个。如果找到了正确的类别,将检查相应的三种权限类型,即读取、写入和执行,以查看它们是否被允许,以及它们是否与用户的尝试读取、写入或执行操作匹配。

使用基于八进制计数系统的快捷方法来更改 9 位权限字符串中的值是最好的。请注意,还有另一种可用的表示法,它使用短选项,如-+rwe,我们在本节中不讨论。您可以使用man chmod查找它。使用 0 到 7 之间的数字,这是八种不同的状态,因此可以称为八进制表示法,我们可以为每个权限类别(用户所有者、组所有者或其他用户)定义每种可能的读、写和执行组合。

以下是chmod的八进制表示法:

  • 0:使用0,不允许读、写或执行权限

  • 1:它表示只有执行权限

  • 2:它表示只有写权限

  • 4:它表示只有读权限

  • 3:它表示执行和写权限的组合

  • 5:它表示只有执行和读权限的组合

  • 6:它表示只有写权限和读权限的组合

  • 7:它表示完全权限或读、写和执行权限

因此,我们可以只使用三个数字轻松表示所有三个权限类别的权限类型。第一个数字表示文件的用户所有者的读、写和执行权限。第二个数字表示组所有者的所有文件权限,第三个数字表示系统中所有其他用户可用的所有读、写和执行权限。例如,八进制权限777表示系统中所有用户的读、写和执行权限。权限775表示文件的用户所有者的读、写和执行权限,文件的组所有者的读、写和执行权限,以及系统中所有其他用户的只读和执行权限。权限660表示文件的用户所有者的读和写权限,文件的组所有者的读/写权限,以及系统中所有其他用户根本没有权限,这意味着他们无法读取、写入或执行此文件。

由于我们在上一节中创建了一些新用户PeterPaul,并分配了project_aproject_b组,现在让我们来实际工作和实验实际文件权限:

  1. 由于我们在本章中正在处理权限,首先以 root 用户登录。

  2. 现在,让我们首先创建一个目录,在其中放入一些文件以便使用:

mkdir test_files  
  1. 然后切换到这个目录:
cd test_files  
  1. 现在,让我们创建一些文件来玩:
touch file1
touch file2
echo "my_secret_pa$$worD" > file3
touch file4
printf '#!/bin/bashnecho "EXEC"' > file5  
  1. 让我们使用ls-l查看文件权限。

现在我们知道,每个文件都有一个文件所有者,在ls -l输出的第三列中可以看到。每个文件的第四列中也有一个组所有者。ls -l输出中的第一个字符是文件类型,后跟 9 位权限字符串。

首先,让我们学习如何更改文件的用户所有者。您可以使用chown命令更改文件的用户所有者。您可以使用chgrp命令更改文件的用户组。让我们再次使用ls -l查看已更改的内容:

如您所见,file1file3已更改了用户所有者,file4有一个新的组所有者。除了使用chgrp命令外,还有一种定义文件组成员资格的替代方法,系统管理员经常使用。它具有以下表示法:

chown [username]:[groupname] [file]  

这使用冒号来指定文件的用户所有者或组所有者。例如,要仅更改文件的组所有者,请使用:

chown :project_b file 4  

或者同时更改用户名和组使用:

chgrp project_a file 4  

让我们也创建一些子文件夹,以便稍后测试目录权限。还将一些文件放入我们新创建的子文件夹中。接下来,让我们对test目录使用一些非常危险的权限,以便在我们的测试期间每个人都能正确地使用文件。这不是为了生产而设计的。

正如我们之前学到的,我们将使用chmod八进制表示法来改变 9 位权限字符串文件权限。接下来,让我们为我们的新测试文件创建一些常见的权限。同时,为了展示一些东西,创建一些不寻常的权限。还要改变我们测试目录的权限:

最后,为了准备我们的测试,我们还需要改变一些目录的用户所有权权限和组所有权权限。现在,让我们玩弄一下我们新文件和文件夹的权限。file1是一个文件,对任何人都有完全的读取、写入和执行权限。这是一个非常危险的权限,在任何情况下都不建议使用,因为任何人都可以修改这个文件:

正如你所看到的,peterpaul可以修改这个文件并对其拥有完全访问权限。接下来的file1file2,具有标准权限,这是每个文件创建时都会得到的权限。文件所有者可以读取和写入,组和所有其他用户只能读取和执行,但不能修改。

让我们看看如果各种用户尝试写入这个文件会发生什么:

正如你所看到的,只有文件所有者可以写入这个文件;所有其他用户都没有写入权限。下一个文件具有经常用于保护机密数据的权限,比如密码文件。

正如你所看到的,只有文件所有者可以读取文件,其他人无法对文件执行任何其他操作。文件所有者是paul。如果你尝试使用各种用户名读取这个文件,你会学到两件事。首先,无论文件设置了哪种权限,root 用户始终可以完全访问文件。其次,除了 root 用户,谁都无法访问这个文件,除了paul,他有读取权限,其他人都不行。

下一个文件具有常用的权限设置。不仅文件所有者,而且文件唯一的组的成员应该对文件有完全控制权:

正如你所看到的,olippeter都有对文件的写入权限,paul无法访问该文件。olip有对文件的写入权限,因为他是文件所有者。peter有对文件的访问权限,因为组所有者也有对文件的访问权限,而 Peter 是project_a组的成员,也是文件的组所有者组的成员。

现在,file5有一些不寻常的权限,这是有效的。file5是一个脚本文件,可以打印出一些东西:

正如我们所看到的,只有 root 用户有权限执行这个文件。为了执行一个脚本,我们将使用./表示法,我们将在另一个部分中看到。为了让peter能够执行一个脚本,我们只需将project_a组添加到file5中,因为我们知道peter是这个组的成员。但等等,为什么当peterproject_a组的成员,而project_a有权限执行脚本时,我们会收到权限被拒绝的错误?这是因为为了让 shell 运行一个脚本,它还需要访问读取脚本文件的内容。所以,让我们改变文件的权限,也包括读取标志。现在,用户peter能够执行这个脚本了。对于root用户,你不需要设置读取权限,因为root用户对每个文件都有所有权限,不管权限字符串中说了什么。

最后,一个常见的误解是,为了删除一个文件,你需要为想要删除文件的用户正确设置文件的写入权限标志,但这是不正确的:

如果这是真的,为什么彼得不能删除这个文件,因为我们给了这里的每个人完全的权限?这是因为文件删除完全取决于要删除的文件所在目录的写权限,而绝对不是任何文件权限。所以,在这个例子中,我们想要删除的文件在root目录中,对于用户彼得来说根本没有写权限。所以彼得无法删除或创建/root目录中的任何文件。

最后,让我们讨论目录权限。让我们首先查看我们的测试文件夹的目录权限。为了这样做,让我们切换到test文件夹的目录。让我们首先测试folderA上的权限:

如你所见,folderA只对文件所有者设置了读权限,所以彼得是唯一能够读取folderA中所有文件和子文件夹的人;其他人都不能。你还可以看到因为目录上没有设置执行标志,没有人能够进入这个目录。

现在,让我们来看看folderB

因为只有project_a文件夹上设置了执行权限,而且只有project_a组的组所有者设置了执行权限,所以只有彼得作为project_a组的一部分才能cd进入这个目录,但你不能列出文件。所以,总是将目录上的执行标志与读标志结合起来是一个好主意。对于folderC也是如此。首先,让我们试一下是否有人能够在这个目录中写入文件:

su peter -c 'touch folderC/a-new-file'  

正如你所看到的,没有人可以。如果你查看文件夹权限,这是因为我们的用户都没有对该文件的所有权权限。

所以,让我们将用户所有权设置为用户olip。仍然没有运气能够用用户olipfolderC中创建一个新文件。这是因为为了在目录中创建一个新文件,不仅需要在目录上设置写权限,还需要设置执行权限。如果你想在目录中删除文件,也是如此。最后,我们如何递归地为整个子目录树更改文件和文件夹权限?为了用一个命令递归地更改folderA中包含的所有文件和文件夹,使用chmod -R标志,它代表递归,并更改作为参数给出的目录中的所有文件和文件夹条目。你也可以对更改所有者命令使用-R标志。一如既往,小心使用递归标志,因为你可能会将文件更改为权限。当涉及到理解 Linux 中的权限时,有三件事你需要记住。这三个概念必须从左到右使用。首先,每个文件对于用户所有者、组所有者和所有其他用户,简称ugo,都有一组权限状态。对于这些类别中的每一个,都存在三种可能的权限状态,读、写和执行,或rwx。读、写和执行可以用八进制数字 4、2 和 1 来表示,分别代表rwx。你想要允许的每种读、写和执行的组合都可以用对应的八进制数字值的和来表示。

处理文本文件

在这一部分,我们将学习在命令行上打印文本文件内容的所有重要工具。我们还将学习如何使用文本文件查看器查看文本文件。在 Linux 中,存在两种不同的基本文件类型,文本文件和二进制文件。文本文件是配置文件,而二进制文件可以是图像文件或压缩数据文件。文件的编码定义了一个文件应该被视为文本文件还是二进制文件。文本文件通常使用 UTFR。在 Linux 上,文本文件通常使用 UTF-8 或 ASCII 进行编码。你可以使用file命令来检测文件类型,比如:

file /etc/passwd 
file ~file4.tar.gz  

要打印出文本文件的内容,可以使用cat命令。cat代表连接,这也是命令名称的原因。因此,让我们连接一些文件,并将结果放入新文件中,通过重定向stdout

cat /etc/passwd /etc/grp /etc/services > /tmp/concatenated-file  

这一行将三个文件passwdgroupservices连接到/tmp目录中的名为concatenated-files的新文件中。有时,使用cat打印整个文件的内容纯粹是过度的。如果我们只对文件开头或结尾的一些行感兴趣,可以使用headtail命令。文件的开头有时也被称为文件头,而文件的结尾也被称为文件尾。要显示我们的新连接文件的前10行,请使用:

head /tmp/concatenated-file  

或者,如果您只对我们的新文件的最后 10 行感兴趣,请改用:

tail /tmp/concatenated-file  

要更改headtail默认的打印前 10 行的行为,请使用-n选项。Head 和 tail 还有其他非常有用的选项,请使用手册页了解更多。一个更重要且经常使用的功能是使用tail follow选项。例如,使用root帐户的follow选项,-f标志保持tail命令打开,并且 tail 将不断监听新的文件内容,并在var/log/messages文件追加新文本时输出。如果您需要对永久写入的文件进行实时查看,这个命令需要被记住。要关闭 tail 程序,请使用“Ctrl”+“C”:

su root -c 'tail -f /var/log/messages'  

现在,要阅读文件的内容,可以使用cat命令来处理较小的文件。对于较大的文件,最好使用真正的文本查看程序,例如less,它具有一些强大的功能,如搜索、滚动和显示行号。学习如何使用 less 命令导航文本文件也非常有用,因为许多 Linux 命令都使用 less,也称为较少导航,以浏览页面或设置的文本内容,我们稍后将看到。要使用 less 打开文件,可以使用 less,然后文件名作为参数。除非使用管道,否则也可以直接使用stdout,这非常有用,因此我们可以轻松地导航和滚动较长的命令输出,这些输出无法适应屏幕。在 less 中导航非常简单,应该被记住,因为您将在 Linux 职业中经常使用它。还有很多要学习的。阅读 less 命令的手册页以查看所有可用选项。

以下是执行许多移动操作的方法:

  • 要向下滚动一行,可以使用箭头键或“J”键。在这里,我们将每个操作只展示一个键盘选项。

  • 要退出 less 命令,请使用“Q”键。

  • 大写的G滚动到文件末尾,而小写的 g 滚动到文件开头。

  • 按下箭头键可以逐行向下滚动。

  • 按下箭头键可以逐行向上滚动。

  • 按下“Page Down”键向下滚动一页,按下“Page Up”键向上滚动一页。

  • 按下右箭头键可以向右滚动更长的行;要向左滚动,请使用左箭头键。

  • 按下“Ctrl”+“G”以在页面底部显示文件信息。

  • 按下“Return”键退出文件信息字段。

  • 键入斜杠键,然后输入搜索词,例如HTTP,然后按下“Return”键进行前向搜索文件中的关键词HTTP

  • 按下“n”键将跳转到下一个搜索结果。按下大写的“N”键将跳回到上一个搜索结果。

  • 请注意,如果搜索模式全部为小写,则搜索是不区分大小写的;否则,它是区分大小写的。例如,如果搜索的单词HTTP全部为大写字母,它只会找到确切以区分大小写形式的 HTTP。

  • 现在,按下大写的“G”跳转到文件末尾。

  • 使用斜杠键进行正常搜索会从文件顶部到底部搜索关键字。

  • 如果您想要从底部向顶部搜索关键字,可以使用问号运算符,问号键,然后是关键字。

  • 按下n键跳转到文件中的下一个更高搜索结果。按下大写N跳转到搜索结果的最后形式。

  • Less -N以行号模式启动 less,这意味着每一行都以相应的行号为前缀。

  • 要转到特定行号,例如,第 100 行,输入行号后跟g,或者要转到第 20 行,输入20g

  • 要查看文本文件而不进行编辑,也可以使用 VIM 编辑器。

  • 要以只读模式启动 VIM,输入 view 空格,然后是文件名。

  • 我们将在下一节中使用 VIM 编辑器。

使用 VIM 文本编辑器。

在本节中,我们将学习如何安装 vi 改进版,简称 VIM,文本编辑器。我们还将学习使用 VIM 的所有基础知识。可以想象的最简单的文本编辑器之一是,这将创建一个名为my-lorum-file的新文件,其中包含lorem ipsum dollar sit,或者您可以使用cat命令交互式地创建一个新的文本文件

Another Line
this is the third line
EOF  

使用大写的字符串EOF停止向该文件写入。现在,如果您需要创建只有几个单词或几行文本的文本文件,echocat命令非常有用。如果您需要编辑更大的文本文件或想要编写自己的文件,例如项目的 read-me 文件,最好使用真正的文本编辑器。Linux 中可用的文本编辑器之一是 VIM,或者改进的 vi,它是每个 Linux 发行版都可以使用的非常强大的文本编辑器。它允许无鼠标文本编辑,一旦您熟练掌握 VIM,您就可以真正开始以思维的速度输入或编辑文本文件。但是,精通 VIM 可能需要几个月甚至几年的时间才能真正掌握,因为 VIM 是一个非常复杂的编辑器,具有许多不同的快捷键和功能。因此,我们无法在本节中展示所有内容,而只能展示基础知识,以便您可以快速开始使用 VIM。

VIM 是 vi 的改进版本,完全兼容 vi,vi 是上世纪 70 年代开发的 UNIX 文本编辑器。在 CentOS 7 最小安装中,默认情况下不安装 VIM。因此,让我们从安装 VIM 编辑器开始- su root -c 'yum install vim -y'。您可以使用文件名作为参数打开 VIM,也可以不带文件名打开,稍后保存文件名。VIM 最基本的概念是其模式。有三种不同的模式。插入模式、命令或正常模式和 ex 模式。以下截图显示了不同的模式:

当您打开 VIM 时,您会进入正常或命令模式。从任何模式,您都可以通过按Esc键返回到正常模式。如果您不知道当前处于哪种模式,这非常有用。只需按下Esc键,您就会始终处于正常模式。从那里,您可以切换到插入或 ex 模式。有几个键可用于启动插入模式。按下io键将您从命令或正常模式带入插入模式,您可以在那里开始输入文本。如果您完成了输入文本,或者想要执行另一个正常模式命令或ex命令,请按Esc键返回正常模式。从那里,如果您按冒号键,您将进入 ex 模式。从那里,按下ReturnEsc键将您带回正常模式。插入模式用于输入或插入文本。在插入模式中,每次按键都会在编辑器中打印。如果您想要导航光标或执行诸如复制和粘贴、删除行、文本搜索、撤消、重做等操作,您需要切换到正常模式。在正常模式中,每次按键都是一个命令。Ex 模式用于执行ex命令,例如,跳转到文件中的某一行,或者在整个文件中进行替换文本。

现在,我们将开始实际使用 VIM。首先,要退出编辑器,在正常模式下按以下顺序,按:q!,然后按Return键。如果你不在正常模式下,例如,你在插入模式下,那么你首先要按Esc键,然后:q!,然后按Return键退出 VI。现在,让我们用/etc/services文件再次打开 VIM。

让我们首先讨论基本的光标移动命令。光标移动命令只能在正常模式或命令模式下执行,这是启动 VIM 时的默认模式。如果你在其他模式下,比如插入模式,按Esc键进入正常模式。现在,要移动光标,你可以使用各种键盘快捷键:

  • 要将光标向右移动,使用l

  • 要将光标向左移动,使用h

  • 要向下移动光标,使用j

  • 要向上移动光标,使用k

  • 你也可以使用箭头键来执行前面的操作

  • 将光标移动到行尾,按$

  • 要将光标移动到行首,按0

  • 要向前移动光标一个单词,使用w

  • 要向后移动光标一个单词,使用b

  • 要跳转到文档末尾,使用大写G键,这也是 less 文本文件查看器中用于跳转到文档末尾的相同键

  • 要跳转到文档开头,输入gg

  • 这也类似于 less 命令,其中你使用小写g一次跳转到文档开头

  • 要跳转到特定行号,请输入行号;例如,第 100 行,然后跟一个大写G

  • 在 VI 编辑器中搜索文本模式基本上与在 less 文本查看器中搜索文本相同

  • 使用/关键字后跟回车键开始向前搜索

  • 按小写n跳转到下一个搜索结果

  • 按大写N跳转到上一个搜索结果

  • 要开始向后搜索,首先按大写G到文档末尾,然后使用熟悉的问号关键字进行搜索,按Return键从底部到顶部开始文本搜索

  • n跳转到下一个更高的搜索结果,按N跳转到文档底部的下一个更低的搜索结果

  • 现在,再次跳转到文件开头

在正常模式中一个非常有用的功能是在特定行设置标记以供引用。例如,首先转到行域。要在特定行设置标记,输入字符m,然后再输入从 a 到 z 的另一个字符。例如,输入ma。这将在以域开头的行中创建一个新的标记,由字符 a 引用。在以域开头的当前行,如果我们在文件中的其他位置,例如,按页向下滚动,然后现在使用引号字符后跟表示我们标记的字符,例如'a,我们将跳转回到我们设置引用标记a的行。如前所述,你可以从 a 到 z 设置多个标记,所以让我们添加另一个标记。只需转到另一行,例如 saft 行。现在,我们将使用b作为我们的标记。让我们创建一个标记,输入mb。现在,如果你转到文件中的其他位置,比如 fido 行,只需输入'b即可滚动回 saft 行。输入'a返回到域行。就是这么简单。

现在我们已经学会了在正常或命令模式下的基本移动命令,让我们现在转而学习一些正常模式下的删除命令。按下x键将删除光标下的字符,同时保持在正常模式下。连续按两次dd键将删除一行并将已删除的文本放入复制缓冲区。d 键也可以与其他键组合以实现高效的文本删除。使用dw删除光标下的当前单词。您甚至可以将dw命令与数字结合使用,例如要删除接下来的五个单词,请键入5dw。您已经知道,为了跳转到行尾,您使用*\(*,而要跳转到行首,您使用*0*。如果要从当前光标位置删除到行尾,请使用`d\)。另一方面,如果要从当前光标位置删除到行首,请使用d0`。

现在,让我们来看看已删除文本的撤消和重做命令。按下 u 键可以撤消上次的更改。对于每个撤消步骤,您也可以使用Ctrl + R执行相应的重做步骤。现在,对于复制和粘贴命令,只需复制和粘贴完整的行类型yyp。要复制多行,首先标记要复制的所有行。为此,按下Shift + V开始标记,然后按下向上或向下箭头键选择要复制的所有行。现在,按下y键复制您的文本,然后按下大写P插入您复制的文本。

您还可以剪切文本。要剪切文本行,首先使用大写V标记您的文本。现在,不要使用y键来复制文本,而是按下大写C键剪切文本。请注意,剪切文本将使您进入插入模式。要粘贴文本,您需要返回到正常模式;因此,现在按下Esc键。要在其他地方粘贴文本,请使用大写P键。

现在我们已经讨论了正常或命令模式下的所有基本命令,让我们转到插入模式。有几种方法可以从正常模式切换到插入模式。通常,要进入插入模式,您可以使用小写的io以及大写的O键。按下小写的o在光标后插入新行并进入插入模式。按下大写的O在光标前插入新行并进入插入模式。按下i会在光标后立即进入插入模式,而不插入新行。

最后,让我们讨论ex命令。让我们首先对文件进行一些更改。现在,为了执行ex命令,我们首先需要进入正常模式,从插入模式按下Esc键,然后按下冒号键开始输入ex命令。例如,要写入文件,请键入w。这将把您的更改写入并保存到文件中。您还可以使用:输入ex命令,然后按wq写入并退出 VIM 编辑器。

现在,让我们再次打开 VIM。要离开编辑器,请按:q,然后按Return键。如果您没有进行任何更改,这将离开编辑器。现在回到 vi 编辑器。使用q ex命令并按回车键只有在您没有对文件进行任何更改时才有效。让我们更改文件。现在,如果您想在对文件进行了一些更改的情况下离开编辑器,使用 ex 命令q将通知您即将离开编辑器而不保存更改。因此,如果您想在不保存更改的情况下退出编辑器,只需键入:q!。现在,从终端返回到服务文件。另一个非常有用的 ex 命令是在 vi 编辑器中执行命令行上的命令。这可以通过使用ex命令感叹号然后是您想要在命令行上执行的命令来完成,例如ls。这将切换到命令行并呈现结果;然后如果您按回车键,您将返回到编辑器。另一个非常有用的 ex 命令是sh命令。在ex命令中键入sh将在后台继续运行 VI 的情况下切换到命令行。在这里,您可以像在命令行上一样执行命令。如果您完成了命令行上的工作,可以通过键入exit返回到 VIM 编辑器。

要搜索和替换单词,VIM 为我们提供了一种类似集合的替换模式:

  1. 如果您想要在整个文件中用单词echo替换为单词hello world,请使用以下命令:
:%s/echo/HELLOWORLD/g  
  1. 要启用行号模式,请键入set number

  2. 要转到特定行,请在 ex 模式下键入行号。

  3. 要离开行号模式,请键入set nonumber

  4. 要在 VIM 中打开不同的文件,请键入e,然后是文件名。

  5. 要将文件另存为不同的名称,请键入w,然后是不同的文件名,例如my-test-file

总结

在本章中,我们广泛讨论了 Linux 文件系统,其中包括文件链接、文件搜索、文件权限、用户和用户组以及 VIM 文本编辑器。我们还深入研究了每个概念的功能。

在下一章中,我们将介绍如何使用命令行。

第四章:使用命令行

在本章中,我们将学习每个 Linux 用户都应该知道的更多基本命令,然后我们将学习如何安装其他重要的第三方 Linux 程序。我们还将学习有关进程和信号的知识,向您介绍 Bash shell 脚本编写,并最终向您展示如何自动执行 Bash shell 脚本。

我们将涵盖以下主题:

  • 基本的 Linux 命令

  • 其他程序

  • 理解进程

  • 信号

  • 使用 Bash shell 变量

  • Bash shell 脚本编写

基本的 Linux 命令

在本节中,我们将学习更多每个 Linux 用户都应该知道的基本 Linux Bash 命令。使用cat命令快速从文本文件中剪切列。这类似于awk的轻量级版本。

我们将讨论以下命令:

  • cat

  • sort

  • awk

  • tee

  • tar

  • 其他杂项命令

首先,让我们创建passwd文件的较小版本,以便使用cat命令:

-d设置字段分隔符;默认情况下是制表符。-f使用要提取的单个字段编号或逗号分隔的字段编号列表。如果使用逗号分隔的列表,分割输入分隔符将被输出,可以使用--output-delimiter进行更改。

接下来,让我们创建一个没有注释和空行的services文件的较小版本。使用cat命令非常受限于文件分隔符是单个字符的特殊用例,例如冒号或制表符。对于拆分包含多个连续空格字符的文本文件,例如在 Linux 配置文件中经常使用的情况,例如在/etc/services文件中,cat命令不起作用。此外,当使用cat命令时,每行的字段顺序必须固定,否则会遇到问题。

在下面的截图中,您可以看到services文件不包含制表符,而是包含多个用星号标记的空白字符:

如果您在此文件上使用cat,它将产生一堆垃圾。要使用awk来拆分具有多个连续空格的文件。tr命令类似于集合替换模式的轻量级版本或子集。它将一个字符集从stdin转换为另一个字符集,并输出到stdout。语法是不言自明的。您可以转换单个字符和字符范围。字符集类似于 POSIX 正则表达式类;请阅读手册以了解更多信息。

让我们讨论sort命令。sort命令按行对文本文件进行排序。默认情况下,它考虑整行进行排序。-u标志只打印出唯一的字段。如果我们使用数字而不是字母数字值的文件,默认情况下,sort期望字母数字值,因此数字的排序是错误的或不自然的。为了解决这个问题,使用-n选项,它使用数字进行排序。要从底部到顶部排序值,请使用-r标志。如果需要,您还可以影响排序列。sort始终考虑整行。为了解决这个问题,使用-k 2.2选项按第二列进行排序。还有许多其他排序选项。请参考手册以了解更多信息。

现在,为了结合catawksortunique的功能,让我们一起使用这些工具来打印/etc/services文件中出现最频繁的 10 个服务名称,同时忽略注释和空行:

第二个命令现在应该是非常容易理解的。正如您所看到的,discardexp1exp2/etc/services文件中最常出现的服务名称,出现了四次。要计算文件中的所有行,请使用wc命令进行单词计数。要从路径中提取纯文件名,请使用basename命令,这在脚本中经常使用,我们稍后将看到。如果您知道文件的扩展名,也可以使用basename命令从扩展名中提取文件名。类似地,要提取路径名,请使用dirname命令。要测量命令所需的时间,请使用time命令执行命令的前缀。要比较两个文件,请使用diff命令,它将打印空输出。如果这些文件相同,将不会有输出。否则,将显示文件之间的更改。diff命令也可以用于比较两个目录,逐个文件使用递归标志进行比较,该标志将遍历来自 A 的所有文件,并将它们与 B 文件夹中具有相同名称的相应大小文件进行比较。可以用于打印特定命令在文件系统中的位置的命令是基于/path变量,稍后我们将看到。

tee是一个有用的命令,可以用于将stdout命令存储在文件中,并在命令行上打印出来。在同时查看输出的同时保留输出记录非常有用。只需将要写入的文件名作为参数给tee命令。要压缩单个文件,即减小文件大小,请使用gzip。要解压缩,请使用gunzip命令。

要递归压缩完整的子目录,请使用tar命令。请注意,f选项必须是后面的选项,后面跟着您要创建的存档名称作为第一个参数,然后是您要存档和压缩的目录作为第二个参数。要将存档提取到任何后续目录,请使用tar命令和以下标志,-C是输出目录。hostname打印主机名;uptime打印服务器计算机已经运行的时间,uname打印系统信息,例如内核版本。

/etc/redhat-release文件中,您将找到此 CentOS 7 基于的 Red Hat Enterprise 的版本。在/proc/meminfo文件中,您将找到内存信息,例如,您有多少 RAM。在/proc/cpuinfo中,您将找到有关 CPU 和核心的信息。free -m打印出有用的内存信息,例如,您有多少可用的 RAM。df打印出有关可用磁盘空间的信息。du -page打印出当前目录中文件占用的空间。如果您使用max-depth=1选项,还将获得文件夹内容的摘要。users打印出当前登录到系统的所有用户。whoami命令打印出当前使用此终端的用户的名称。

现在,我们将看到一些非常有用的命令。要打印当前日期和时间,请使用date命令。使用+%s生成唯一的时间戳。要打印日历,请使用cal命令。要暂停,中断 shell 执行,请使用sleep命令。dd程序,或磁盘转储,是每个 Linux 用户都需要了解的非常重要的工具。它用于从输入文件或设备复制数据到输出文件或设备。我们之前使用过dd,在第一部分中,用零覆盖文件系统的空闲空间,以便我们可以缩小 VM 映像,但dd命令还有许多其他用途。dd基本语法使用if作为输入文件和of作为输出文件的参数。还有两个非常重要的选项,块大小和计数。

您会看到块大小,即一次读取的数据量,为 1 MB,计数是块大小的重复次数,因此,在我们的示例中,1 MB 乘以 1,024 等于 1 GB。dd还支持从stdin读取和写入stdout,因此我们刚才使用的命令可以重写为dd if=/dev/zero of=/tmp/1gig_file.empty bs=1M count=1024。您不仅可以使用dd与设备文件一起使用,还可以使用它来复制普通文件。此外,您还可以使用它来创建整个分区的映像,例如用于备份。要访问分区,需要 root 帐户。

额外的程序

在本节中,我们将向您展示一些其他非常重要的 Linux 命令,您不想错过。这些程序未包含在 CentOS 7 最小安装中,因此我们首先需要安装它们以安装它们。本节是关于学习额外的命令行程序。额外的因为这些工具未包含在 CentOS 7 最小安装中,因此让我们首先使用 CentOS 7 软件包管理器yum安装所有这些程序。为了安装新软件,需要 root 用户。因此,首先以root身份登录。在开始之前,让我们安装epel存储库,这是一个额外的第三方存储库,用于存储官方 CentOS 7 源中找不到的软件,但非常值得信赖和安全。

首先,让我们安装一些工具,使我们的用户生活更轻松。rsync是一个文件传输程序,pv是管道查看器;git用于版本控制;net-tools包含显示网络信息的工具;bind-utils包含查询 DNS 信息的工具;telnetnmap用于基本的网络故障排除;nc代表 netcat,wget用于从互联网下载文件;links是一个命令行网页浏览器。

接下来,让我们安装一些可以让您对系统进行实时查看的程序。这将安装htopiotopiftop。最后,让我们安装一些基本工具,包括屏幕,计算器,bclsof。首先,让我们介绍rsync。每个 Linux 用户都需要了解它,因为它是一个具有许多有用功能的强大工具。基本上,rsync是一个文件传输程序,但它不仅仅是在源和目标之间复制文件;相反,它会将它们同步,这意味着它只在源文件与目标文件不同时传输文件。这节省了大量的数据开销和时间。我经常使用rsync-rav标志,这是默认使用的参数集,用于详细和递归地复制文件。

cpolip-home文件夹递归地复制到新位置。现在,如果您更改源文件并在之后重新启动复制过程,rsync首先检查源文件和目标文件之间是否有任何差异,只传输更改部分:

如前面的截图所示,我们触及了 olip-home 目录中的 bashrc 文件,这意味着更新了文件的时间戳,然后 rsync 检查并发现 bashrc 文件的时间戳已更新,因此文件会再次传输到目的地,因为它已经不同了。要远程复制文件到另一台运行 SSH 服务的服务器,并且已安装了 rsync,使用以下语法:rsync -rav。如你所见,IP 地址末尾的冒号表示目的地。在这里,我们将 olip-home 目录复制到 /tmp 目录,反之亦然,要将远程文件复制到本地服务器,使用 rsync .rav /home/olip/ /tmp/new-olip-homersync 有很多不同的功能,非常棒。你可以参考手册了解更多。我经常使用的另一个有用的工具的例子是 --progress 标志,它可以显示文件传输的进度。pv 是管道查看器,是一个非常有用的程序,可以显示通过 stdout 的流量。例如,我们可以用它来显示在传输大量数据流时的进度,比如使用 dd 命令。git 是一个文件版本控制程序,可以帮助你跟踪文件版本,也可以用于从 Git 仓库安装程序,比如非常流行的 GitHub 服务。例如,我们可以使用以下命令下载最新的 pv 源代码:$ git clone https://github.com/icetee/pv.git

net-tools

net-tools 是一组重要的工具,用于显示与网络相关的信息,比如 netstat 用于打印网络信息,或者 route 命令用于查看 IP 路由表。我们刚刚安装的 bind-utils 包含了浏览 DNS 信息的程序,例如,可以查看某个域上是否开放了某个端口,比如在 www.google.com 上的端口 80;你将会得到一些连接细节。按 Esc 键退出。wget 是每个系统管理员都需要了解的最基本的工具之一。它可以用来从互联网上下载文件。例如,要从 HTTP 下载一个随机的编程命令到 stdout,可以使用以下命令行:wget -q0- http://whatthecommit.com/index.txt,或者直接输入到一个新文件中:wget -0 /tmp/output.txt http://whatthecommit.com/index.txt

Nmap

Nmap 是另一个非常有用的工具,可以用来排除故障或获取有关网络的信息。它扫描计算机网络,发现并收集与其连接的其他主机的各种信息。请注意,端口扫描网络是一个非常有争议的话题;因为不正确地使用 nmap 可能会让你被起诉、被解雇、被国家禁止,甚至被监禁,我们只会用它来检索关于我们自己私有网络的非常有价值的信息。例如,要扫描网络上所有可用的主机和开放的端口,使用以下语法:nmap 网络地址。

你将看到一些可用的 IP 地址,它们有各种开放的端口和服务。这可以为你提供非常重要的信息,关于谁连接到你的网络,以及服务和计算机是否安全,是否暴露了不需要的细节。nc 或 netcat 是另一个非常有用的工具,可以帮助你调试和排除服务器的网络和防火墙设置。例如,你可以用它来查看服务器上是否开放了某个端口。在服务器上,你想要验证使用,例如,以下命令用于打开端口 9999 并在该端口后面放置一个文本文件流:nc -l -p 9999 < /etc/redhat-release。在这个网络中的任何其他服务器上,你可以尝试访问该服务器,例如使用 IP 地址 197,然后使用 IP 地址 192.168.1.200 上的端口 9999 并将该文件流回传,使用以下 nc 命令:nc 192.168.1.200 9999 > /tmp/redhat-release

links

在这个小节中,我们将学习关于links——命令行网页浏览器。要使用 links 程序打开 DuckDuckGo 搜索网站,请使用以下命令行:links duckduckgo.com/。这将打开 links 网页浏览器。移动光标上下以到达 DuckDuckGo 文本搜索字段。现在,你可以像在普通的 DuckDuckGo 网站上一样输入你的搜索词,然后按Enter键开始搜索。再次使用上下箭头键跳转到你想要浏览的搜索结果。学习 links 导航和快捷键超出了本书的范围。按q键退出 links,然后按Enter键确认你的选择。

iotop

要实时查看系统的输入和输出,或者简称 I/O,带宽使用情况,输入iotop。iotop 需要使用 root 用户启动。你可以使用iotop,例如,了解你的硬盘读写速度有多快,然后按q键退出。阅读iotop的手册部分,了解更多关于它的快捷键,例如用于排序列。

iftop

让我们来了解一下iftop程序,它可以实时查看网络流量和网络带宽使用情况,并进行监控。同样,这个工具需要使用 root 用户账户启动。如你所见,可以使用这个工具显示网络流量,按q键退出程序。阅读iftop的手册部分,了解更多关于它的快捷键。

htop

现在,让我们启动htop,它类似于著名的 top 程序,可以交互式查看进程。htop是正常 top 程序的改进版本,增加了新功能,例如垂直和水平滚动,这样你就可以看到系统上运行的所有进程以及完整的命令行。htop程序会显示关于你的系统的许多不同信息。按q键退出程序。有许多不同的快捷选项要学习;阅读手册页面以了解更多。

lsof

要打印出所有打开文件的列表,也就是当前访问文件的程序,请使用lsof命令。你会得到一个很长的列表;最好使用grep来过滤内容。要在命令行上快速进行一些数学计算,请使用 PC 计算器。screen是一个非常有用的命令,可以从 SSH 连接中分离出来,而不会实际断开或退出连接,这对于暂停工作然后稍后回到完全相同的位置,或者从另一台计算机工作非常有用。这可以节省大量时间。首先,为了创建一个新的可分离会话,请输入screen。现在做你的工作,例如,在 VI 中输入文本。现在,想象一下你的工作日结束了,你回家了。如果没有 screen,现在你需要保存你的更改,关闭 VI,并从服务器注销。有了 screen,只需使用组合键Ctrl + A + D从当前 SSH 会话中分离出来。如果成功从会话中分离出来,会出现一行显示detached from,然后是 screen 会话 ID。现在,为了证明我们可以重新附加到这个会话,只需从服务器注销,然后重新登录到服务器。然后,在服务器上输入 screen -list 以获取所有分离的屏幕的列表。要重新附加到你的屏幕,使用 screen ID:$ screen -r 23433.pts-l_localhost。如你所见,我们恢复到了离开的地方。如果要停止你的屏幕会话,请输入exit。在这里,我们向你展示了这些程序的最基本用例。

理解进程

在本节中,我们将向您展示 Linux 中的进程如何工作。现在,让我们讨论有关进程的一切。在 Linux 系统中,当前正在运行的每个程序都称为进程。一个单独的程序可以由多个进程组成,并且进程可以启动其他进程。例如,正如我们已经知道的那样,Bash shell 本身就是一个命令,因此在启动时会得到一个进程。您在此 shell 中启动的每个命令都是由 shell 进程启动的新进程。因此,例如,每次我们执行la -al命令时,Bash shell 进程都会创建一个新进程,其中ls -al命令正在运行。在每个 Linux 系统上,都有许多进程一直在运行。如果您有多处理器 CPU 计算机,则其中一些进程确实一直在并行运行。其他进程,或者如果您有单处理器 CPU,则只是半并行运行,这意味着每个进程只在 CPU 上运行几毫秒然后暂停,这也被称为进入睡眠状态,因此系统可以在一小段时间内执行下一个进程。这个系统允许所有进程看似并行执行,而实际上它们是依次顺序处理的。

Linux 系统中的所有进程都是由另一个进程创建的,以便每个进程都有一个创建它的父进程。只有第一个进程没有父进程,在 CentOS 7 中是systemd进程。要获取所有运行中进程的列表,请运行ps命令。这里我们使用-ev选项,并将其输出管道传输到less命令,因为它不适合屏幕。您将看到每个进程都有一个称为进程标识符或 PID 的唯一标识符。第一个进程,systemd 进程,具有 PID 1。随后的进程按递增顺序排列。每个进程都有一个与之关联的用户 ID,而且每个进程都有一个由父进程 ID 列表示的父进程。您会注意到列表中的前两个进程的父 PID 为 0,这意味着它们没有父进程。

为了更好地理解父子进程关系,您可以使用pstree命令,我们首先需要使用psmisc软件包进行安装。之后,只需启动pstree命令。通过它,您可以更好地了解哪个父进程创建了哪个子进程,以及进程之间的关系。如前所述,systemd 进程是系统中的第一个进程,它创建了系统中的所有其他进程。每个进程也有一个状态;输入man ps并转到状态部分。最重要的状态是running。这意味着进程当前正在运行,并将由 CPU 执行,或者在运行队列中,这意味着它即将启动。如果进程执行在等待队列中的下一个进程的情况下被中断,您将看到sleeping,或者stopped,甚至defunctzombie,这意味着进程已终止,但父进程尚不知道。

在前一节中,我们已经学习了,您也可以使用tophtop命令来动态或实时查看系统中的进程。状态列显示进程的状态,其中r代表运行,s代表睡眠,等等。如果创建了一个新进程,父进程将被克隆或复制到子进程,因此它与父进程具有完全相同的数据和环境。只有 PID 会有所不同,但父进程和子进程彼此完全独立。

克隆

在 Linux 中,克隆一个进程也被称为forking。例如,如果你在 shell 中执行一个命令,比如sleep命令,一个新的进程就会被创建,与父 Bash shell 进程相同,其中sleep命令被执行。通常,父进程,例如我们的例子中的 Bash shell 进程,会等待直到子进程完成。这就是为什么在子进程运行时你得不到一个交互式的光标。这是你在 shell 中运行每个命令的正常行为。如果你的 Bash 命令行提示被阻塞,这也被称为运行一个前台作业。要杀死这个前台作业,按下Ctrl + C。你也可以通过在任何命令的末尾设置&符号来影响这个前台行为。所以,让我们用&符号重新运行上一个命令。当在命令的末尾使用&符号时,父进程不会等待子进程完成,而是两个进程现在并行运行。这也被称为在后台运行一个进程。你会注意到,在后台运行一个进程会返回子进程的进程 ID,所以我们以后可以引用它。例如,要杀死它,使用kill命令。为了将最后一个后台作业放到前台,再次输入fg并按下Enter键。现在,我们的sleep命令又回到了前台。要将它放回后台,按下Ctrl + Z。这并不是直接将我们的前台进程放到后台,而是暂停了进程。要将一个暂停的进程放到后台,输入pg,或者放到前台,输入fg。为了杀死任何暂停或后台作业,你可以使用kill命令。我们在后台运行的进程也被称为作业。要列出你当前在终端中拥有的所有作业,你可以使用jobs命令。如果你在后台有任何正在运行的作业,输出将显示出来,你可以用括号中的数字引用它。为了处理这样的作业 ID,你需要在前面加上一个百分号。例如,要杀死作业 ID 为 1 的作业,输入kill %1。请注意,我们刚刚使用的pgfgkill命令只在你在终端中只有一个当前后台作业时才有效。如果你在当前终端中使用多个作业,你需要使用百分号分别处理它们。

信号

信号被用于进程之间的通信。如果你启动一个新的进程,当它运行时,你如何通过你的 shell 或任何其他程序或进程与它通信?另外,父进程如何知道子进程何时结束?例如,你的 Bash 如何知道ls -al命令何时终止?在 Linux 中,这种通知和进程间通信是通过信号来完成的。在 Linux 中,如果一个进程启动另一个进程,父进程会被挂起,直到子进程命令完成,这将触发一个特殊的信号,这将唤醒父进程。父进程被挂起,以便不需要等待活动的 CPU 时间。一个常用的信号是中断信号,每当我们在一个活动程序中按下Ctrl + C时,它就会被发送到正在运行的进程。这将立即中断和停止进程。我们已经发送的另一个信号是通过按下Ctrl + Z来挂起一个进程,以便我们可以将它放到后台。除了使用键组合发送信号,你也可以直接使用kill命令向正在运行的进程发送各种信号。

杀死

要获取可以发送给进程的所有可用信号的列表,请使用kill -l。例如,发送给程序以终止它的标准信号是SIGKILL信号,其信号 ID 为 9。因此,让我们首先创建一个新进程,然后终止它;例如,在后台启动一个新的 sleep 进程。正如你已经学到的,将一个进程放入后台会打印出进程 ID。大多数情况下,我们使用kill命令来终止系统进程,这些进程通常不是由我们的用户启动的。因此,检索的标准方法是使用ps选项aux,然后按进程名称进行过滤。使用带有选项auxps会打印出完整的命令行,这通常有助于区分正确的进程,因为通常在这个列表中有多个具有相同命令名称的进程。在我们的示例中,我们只有一个正在运行的 sleep 进程,我们可以确认正确的进程 ID。现在,为了终止这个进程,使用kill -9发送SIGKILL信号,然后是进程 ID。让我们再次使用ps命令来确认:

如你所见,sleep命令已成功终止。在前面的部分中,我们使用了kill命令和百分比作业 ID,但是使用 PID 而不是作业 ID 的kill命令有什么区别呢?后台和挂起的进程通常通过作业编号或作业 ID 进行操作。这个编号不同于进程 ID,因为它更短。使用 PID 杀死进程通常用于使用 root 账户杀死出现故障的系统进程。此外,一个作业可以由一系列或同时并行运行的多个进程组成。使用作业 ID 比跟踪单个进程更容易。

挂起

最后,让我们讨论SIGUP信号,或者挂起信号。在 CentOS 7 中,如果你在后台运行一个程序,比如sleep命令,并且退出系统,然后再次登录,你会看到命令或进程仍在运行。因此,在 CentOS 7 中,我们可以轻松地运行后台进程并退出 SSH 会话,这对于需要一直运行的程序或需要进行几个小时、几天甚至几个月的大量计算非常有用。在其他 Linux 发行版中,如果你退出系统,内核会向所有正在运行的后台进程发送挂起信号,或者简称为SIGUP,并终止它们。在这样的系统中,要禁用发送给你的进程的挂起信号,使用nohup;在你的命令前加上nohup命令,比如nohup sleep 1000 &。这样你就可以安全地退出系统,你的任务不会停止运行。但是,如前所述,在 CentOS 7 系统中,你不需要这样做。

使用 Bash shell 变量

在本节中,我们将向您介绍 Linux Bash shell 变量。Bash shell 变量是一种为任何动态值提供符号名称的好方法,因此我们可以通过名称引用值。这有助于创建非常灵活和方便的系统,您通常只需更改一个值,访问此值的计算机上的所有进程都可以自动更改其行为。使用 shell 变量提供了一种简单的方法,在 Linux 中多个应用程序和进程之间共享配置设置,我们将在下一节中看到。要定义一个新的环境变量,请使用以下语法MY_VALUE=1,变量的名称等于,然后是值。所有 Bash shell 变量都不得包含空格或特殊字符,并且按照惯例,通常 shell 变量都是大写的。要访问存储的 shell 变量的值,这只是存储值的 shell 扩展,用美元符号作为变量的前缀。您还可以将 shell 变量的内容复制到另一个变量中,使用以下语法:MY_NEW_VALUE=$MY_VALUE。要取消设置 shell 变量的内容,请使用unset命令。对于分配 shell 变量,与我们在前几章的 shell 引用和 globbing 部分学到的任何其他 Bash 主题一样,都适用相同的引用和转义规则。例如,首先将字符串b分配给 shell 变量a。现在,为了在字符串中嵌入空格,必须使用引号。在字符串中嵌入双引号时,使用单引号来包围。有许多预定义和全局的 shell 环境变量来配置系统范围的设置,例如homepathshell等。

虽然 Linux 中没有大多数环境变量的官方标准,但很多程序都使用常见的变量名。例如,如果为PROXY环境变量设置一个值,所有使用该变量的程序和服务现在都可以访问这些新的集中信息,而无需您单独告诉每个程序或服务有什么变化。另一个非常重要的系统环境变量是PATH变量。它被 Bash shell 本身使用。它包含所有由冒号分隔的路径,Bash shell 尝试查找可执行文件的位置,因此您不必为包含在此路径中的命令提供完整路径。例如,如果我们在一个名为my-script.sh的新本地脚本文件夹中创建一个新的脚本文件,我们需要提供其完整的名称位置才能执行它;没有其他方法可以执行脚本。但是,我们不能从/tmp目录运行它,因为 Bash 在其路径中找不到它。现在,如果我们将脚本的位置添加到路径环境变量中,我们就可以在任何地方运行我们的脚本,而无需提供完整的路径,甚至自动完成也可以工作。但是 Bash shell 变量和环境变量之间有什么区别呢?

普通的 shell 变量不是所谓的进程环境的一部分,换句话说,在任何子进程或子进程中都看不到它们。这是因为在执行进程时,只有环境被克隆,而不是本地 shell 变量。您可以通过使用MYVAR=helloworld创建以下 shell 变量来测试这一点,然后在我们将作为子进程运行的脚本中使用它:

正如您所看到的,我们创建了一个名为MYVAR的新 shell 变量,然后创建了一个引用或尝试访问此环境变量的脚本。现在,如果我们执行此脚本会发生什么?正如您所看到的,子进程或子进程无法访问父进程中的MYVAR Bash shell 变量,但是您可以通过将我们的MYVAR shell 变量定义为环境变量来更改此行为。在进程创建期间,任何子进程都会获得父进程的环境副本,包括所有环境变量,但不包括本地 shell 变量。如果您在 shell 变量前加上export这个词,子进程就可以访问这个环境变量,因为在创建新进程时,环境是从父进程复制到子进程的。但是,即使像 shell 变量这样的环境变量也无法在系统退出时保留,这意味着如果您关闭 SSH 会话,所有定义的变量都会消失。

如果您想创建一个系统范围的环境变量,该变量对每个用户都可用,并且在系统退出时仍然存在,请使用您的 root 用户帐户将变量放入/etc/environment文件中。您还可以使用以下语法通过在运行命令之前在 shell 变量名称前加上前缀的方式使 shell 变量对子进程可用,例如MYVAR=NEW_Helloworld ~/scripts/local_var.sh。这样,您就不必将 shell 变量定义为环境变量。另一个非常重要的规则是,子进程永远无法更改父进程的环境变量,因为子进程和父进程彼此独立,子进程只有父进程环境的本地副本。要测试这一点,请尝试以下操作:

首先,让我们清除本地子 Bash shell 变量可能具有的所有可能的旧值。接下来,创建一个脚本,创建一个名为CHILDVAR的新环境变量,其值为Hello_from_child。现在,如果我们执行此脚本会发生什么?如果执行脚本,CHILDVAR环境变量将在子进程中设置,并且此CHILDVAR环境变量对父进程不可见。总之,在脚本中定义的任何 shell 变量或环境变量永远不会在父进程中可见。如果要使 shell 变量从子进程可用于父进程,首先需要在子进程中创建一个所谓的源文件,在其中定义您的环境变量vi ~/scripts/child.sh

接下来,在您的子进程中执行脚本:

这将为父进程创建源文件。现在,在父进程中,首先我们检查CHILDVAR环境变量是否可用。如果没有,让我们使用source命令来源化它。最后,让我们重新检查CHILDVAR环境变量是否现在可访问。如果是,那么这是在子进程中创建环境变量并使其可用的有效方法。

Bash shell 脚本介绍

在本节中,我们将向您介绍 Bash shell 脚本的核心概念。Bash shell 脚本的另一个非常重要的特性是函数。我们在 Bash shell 脚本中大量使用函数,以使重复出现的任务或命令可重用。函数封装了一个任务,使其更加模块化。函数通常接收数据,处理数据,并返回结果。一旦编写了一个函数,它就可以一遍又一遍地使用,但我们也可以在命令行上使用函数。

让我们通过创建一个来讨论函数的一般语法:

$ say_hello90 {
>echo "My name is $1";
>}  

第一个单词是函数名,后面跟着开括号和闭括号,用于定义函数,然后是一个大括号;所有属于函数的命令都在开括号和闭括号内定义,这也被称为函数体。函数可以像普通命令一样有参数,这些参数可以从外部访问函数体。要在函数中访问特定参数,使用美元符号加数字的表示法。所以$1是第一个参数,$2是第二个,依此类推。让我们来看看我们的say_hello函数。如果我们用一个参数调用这个函数,函数将用一个参数执行,并且这个参数将在函数体中被使用,我们可以用$1变量访问第一个参数,这不过是一个普通的 shell 扩展。

函数也可以在它们的函数体中调用其他函数。现在,让我们学习如何将你的 shell 命令放入一个 shell 脚本文件中。脚本文件只是包含不同 Linux 命令、控制结构、循环等的纯文本文件。通常,它们是为了解决日常计算机问题并满足你自己的个人需求,而不是手动逐个执行单个命令。有两种方法可以将文本文件作为 shell 脚本执行。第一种方法是将其作为 Bash 命令的参数使用。另一种方法是在不使用它作为 Bash 命令的参数的情况下首先使脚本可执行,然后在第一行放置所谓的 shebang 行,告诉命令行这个文件是一个 Bash 脚本,并且应该用 Bash 解释器启动。在我们的例子中,#!/bin/bash是 shebang 行,告诉 Bash 这是一个 Bash shell 脚本。现在,要用 shebang 方法启动它,使它可执行,然后你可以在命令行上运行它,如下:

$ vi /tmp/new-script.sh
$ chmod +x /tmp/new-script.sh
/tmp/new-script.sh   

与使用函数类似,我们也可以在 shell 脚本中访问命令行参数,比如$ vi /tmp/new-script.sh。第一个参数可以用$1访问,第二个参数用$2,依此类推。在 shell 脚本中,你还可以用$0访问 shell 脚本的名称。可以用$#访问参数的总数。所以,例如,要编写一个检查你的脚本至少需要两个参数的检查,做如下操作:

#!/bin/bash
echo "Hello World"
echo "..........."
if [[ $# -lt 2 ]]
then
echo "Usage $0 param1 param2"
echo $1
echo $2
echo $0
echo $#  

所以,这个脚本的作用是检查命令行参数的数量是否至少为两个,如果不是这样,那么将打印出一个使用格式,说明你需要两个参数,按Enter,然后返回一个值为1的退出值,这意味着这个脚本抛出了一个错误,因为,正如我们已经知道的,脚本将在成功执行时返回0。让我们测试一下这个脚本:

如果我们只用一个参数启动脚本,它将打印出使用格式。然而,如果我们用两个参数启动它,它将正确工作。当涉及 shell 脚本时,还有很多东西要学习,我们只能向你展示最基本的东西来让你开始。你可以参考 Bash 手册,或者开始阅读你的 Cent0S 7 操作系统中免费提供的各种 shell 脚本。输入以下命令以获取所有.sh文件的列表:su -c 'find / -name "*.sh"',这是你系统中 shell 脚本文件的默认扩展名。只需打开系统中可用的一个 shell 脚本文件,并尝试理解它,例如/usr/libexec/grepconf.sh

实施 Bash shell 脚本

除了我们在前一节中使用的逻辑andor表达式,如果我们需要根据命令的退出状态、变量值、命令输出等做出决定,我们需要理解if语句或条件分支。简而言之,if语句意味着基于某些条件,我们的脚本或命令行应该执行一个动作,否则应该执行其他动作。

让我们再次使用上一节的退出代码来演示:

在这个例子中,我们发出了ls命令来查看oiip主目录的内容。我们将ls命令的退出状态存储在EXIT Bash 变量中。在下一行,我们现在陈述了 if 条件。这可以理解为:if Bash 变量EXIT等于0,那么打印出两行文本,以及这个if条件与反向 if 词 fi。正如你所看到的,这两行已经被打印出来,这意味着 if 条件是真的,所以退出值是0。重要的是要注意,你必须非常小心地设置空格和换行,就像我在前面的例子中所做的那样,但你也可以把完整的 if 语句放在一行中,你可以看到如果按上箭头键显示历史中的最后一个命令。正如你所看到的,shell 在内部使用分号空格而不是换行来分隔大多数表达式,这有点难以阅读,特别是当你写更复杂的 Bash shell 脚本一行代码时。要否定任何 if 表达式,这意味着if语句在条件不满足时评估为真,使用以下内容:

$ EXIT=1
$ if ! [[ $EXIT -eq 0 ]]
>then
>echo "EXIT value is not zero"
>fi
EXIT value is not zero  

在这个例子中,if 条件可以理解为:如果退出值不等于0,那么打印出文本。在我们的例子中,这是真的,因为退出值是1。if 条件可以包括许多不同的测试,这里展示了最重要的一些。要测试相等性,使用-eq测试,就像我们刚刚看到的那样。你可以用它来比较数字。对于字符串比较,使用==运算符。你还可以使用逻辑andor表达式,就像在上一节中介绍的那样,例如,也可以测试替代方案。这个例子可以理解为:如果密码等于Hello_my_world_555或者密码等于my_secret_pass。在这个例子中,密码是正确的。你还可以使用等于波浪线运算符来使用正则表达式。这个语句可以理解为:如果字符串与行的开头匹配,则 if 条件为真,其中第一个两个字符是变量,但接下来必须是rem,这是真的。对于数字值,你也可以使用-lt-gt来测试小于或大于数字,而不是使用-eq,例如,测试小于或测试大于。

另一组非常重要的 if 条件是文件测试。存在大量非常强大的文件测试,用于查看文件或目录是否满足特定属性。有大量非常强大的文件测试,用于查看文件或目录是否满足特定属性,例如测试文件是否存在,使用-a文件测试,或者检查目录是否存在使用-d文件测试。这在下面的截图中显示:

要了解所有现有文件测试,以及所有可用的比较运算符,请打开 Bash 手册并搜索条件表达式。我们刚刚学到的最简单的 if 语句的一般语法是,如果条件为真,则在开始 if 和结束fi之间的所有命令都会被执行。现在,你也可以加入一个else分支,如果条件不为真,则会执行该分支。下面的截图显示了执行情况:

else分支由else关键字引入。在我们的例子中,if 条件不为真,所以将执行 else 分支。如果你有几个独立的条件要检查,你也可以使用elif语句,这比连续写多个if语句要好。所以,不是写三个单独的if语句来检查等于、小于和大于的条件,而是使用更紧凑的elif表示法:

接下来,我们将讨论循环。Bash shell 中最重要的循环之一是for in循环。它可以用于迭代一系列单词。单词的分隔符可以是空格或换行符。现在,如果我们在for循环中使用这样一个以空格或换行符分隔的单词列表,它将迭代列表中的每一项,我们可以在for循环的主体中使用当前值,在那里我们也可以执行命令。这个块将重复多次,就像我们在这个列表中有多少元素一样。在我们的例子中,循环变量的名称,我们称之为count,是可以自由选择的:

这个例子可以理解为:对于1234的列表,每次迭代都将当前值保存在计数变量中,然后在循环体中打印出其内容。但是for in循环可以做什么呢?例如,以下 Bash 内置命令扩展为连续数字的列表:$ echo {1..20}。您也可以使用seq命令做同样的事情,但这会产生一个以换行符分隔的列表。因此,如果我们需要运行一个循环,我们可以这样做。换行符分隔的列表可以完成所有工作,但不要忘记将命令放在美元括号表示法中。正如我们已经知道的那样,shell 通配符字符输出由空格分隔的所有文件的列表,因此我们也可以这样做。在for in循环中使用文件的一个重要用例是重命名多个文件,例如,在具有不同文件扩展名的目录中。请注意,在此示例中,我们使用basename命令并将其放在美元括号表示法中以返回纯文件名:

如您所见,我们创建了一个具有扩展名.txt的新目录,其中包含五个文件。然后,我们使用for each循环循环我们的五个文件,并且对于每个文件,我们将文件移动到 doc 扩展名。还有其他非常重要的循环,比如while循环。您可以参考 Bash 手册并搜索 while。

自动化脚本执行

在本节中,我们将向您展示如何自动化 Bash shell 脚本执行。Cron 系统在每个 Linux 系统上都可用,允许管理员根据任何小时、天甚至月份来确定预定义的计划,从而自动化命令或脚本。它是 CentOS 7 操作系统的标准组件,在本节中,我们将向您介绍管理重复任务的概念,以便利用这个宝贵的工具。

首先,让我们创建一个新的脚本,它将从令人难以置信的Commandlinefu网页下载一个优雅而有用的 Linux 命令行示例,并将其放入 Linux 系统中的motd或每日消息文件中,以便用户每次登录系统时都可以看到。motd文件是一个简单的文本文件,其中的内容将在成功登录时显示。然后,我们将脚本作为 cron 作业运行,以便每天更新一次每日消息,这对于每天学习一个新的优雅的命令行解决方案非常有用。

为了做到这一点,首先以root身份登录,因为 cron 系统位于系统目录中。接下来,复制原始的motd文件。之后,让我们创建我们的脚本文件来更新系统中的motd文件:

#!/bin/bash
Wget -0 /etc/motd http://www.commandlinefu.com/commands/random/plaintext

这个脚本是普通的批处理脚本,从网页www.commandlinefu.com/commands/random/plaintext下载一个随机的 Commandlinefu 示例,使用wget程序将下载的文件保存为/etc/motd。因此,我们可以在登录系统时直接看到内容。现在,让我们测试一下我们的新脚本:

正如您所看到的,脚本已成功从www.commandlinefu.com/网页下载了一个 Commandlinefu。为了测试我们使用的 Commandlinefu 网页 URL 是否真的返回一个随机的命令行示例,让我们重新启动我们的脚本:

正如您所看到的,这次的命令行示例是不同的。现在,根据您自己的脚本执行偏好,您需要决定要多久执行一次脚本。文件系统中有一些特殊的 cron 目录用于执行系统范围的 cron 作业,您可以使用# ls /etc/cron* -d来访问它们。这些文件夹称为cron.dailycron.hourlycron.weeklycron.monthly,它们位于/etc目录中,它们的名称指的是它们运行的时间点。因此,如果我们希望我们的新 Commandlinefu 脚本每天启动一次,只需将脚本文件放入cron.daily目录中,或者使用cd /etc/cron* -d创建一个符号链接。如果您希望使用不同的时间表运行它,只需将其放入cron.hourlycron.monthlycron.weekly目录中。如果您不想再执行它,只需从文件夹中删除脚本或符号链接。如果您不想运行系统范围的 cron 作业,您也可以作为普通用户使用crontab命令。您可以阅读crontab手册以了解更多关于这个命令的信息。最后,让我们测试一下motd文件是否工作。退出 SSH 会话,然后重新登录:

正如您所看到的,它运行得很好。根据我们创建的 cron 作业,明天应该会在这里呈现一个不同的命令行示例。

在本章中,我们向您介绍了用于脚本自动化的 Linux cron 系统。

总结

在本章中,我们讨论了从基本的 Linux 命令、信号、进程到 Bash shell 脚本的各种主题。

在下一章中,我们将介绍高级命令行概念。

第五章:更高级的命令行和概念

在本章中,我们将看一下以下内容:

  • 基本网络概念

  • 安装新软件和更新系统

  • 服务简介

  • 基本系统故障排除和防火墙

  • ACL 简介

  • setuidsetgidsticky bit

基本网络概念

在本节中,您将学习 Linux 中网络的基础知识。关于网络的一切都在 Unix 和 Linux 的经典领域内,事实上,老的 Unix 人说 Unix 是为了网络通信而创建的。Linux 被认为是使用、学习、测试、玩耍、诊断和排除计算机网络的最佳系统之一,因为 Linux 中有很多免费的优秀工具,这些工具可以直接使用,或者只需要一个命令来安装。关于计算机网络的主题有很多要学习的地方,在这里我们只能使用 CentOS 7 Linux 操作系统来教授您其中的基础知识。

现在,让我们从一万英尺高处了解计算机网络。网络或子网络和 IP 地址是网络中最基本的两个概念。每个 Linux 用户需要知道的三个最重要的事实是网络或有时称为子网络,IP 地址和网络规则:

  • 规则 1:网络

每个网络或有时称为子网络,都有一个由数字组成的所谓的网络地址,看起来像这样:

  • 规则 2:IP 地址

每台计算机需要一个 IP 地址进行通信,这个 IP 地址是子网络地址的一部分。在我们的例子中,由点分隔的前三个数字在 IP 地址和网络地址之间是相同的:

  • 规则 3:相同的网络

两台或多台计算机之间进行网络通信的最简单方法是将它们物理连接(例如,使用网络电缆和单个交换机),然后将它们放在同一个网络中,这意味着选择所有计算机的 IP 地址都来自与我们子网络的网络地址相同的范围。在我们的例子中,选择10.0.2作为所有 IP 地址的前三位数字。正如您所看到的,只有最后一位是可变的。然后,想要与同一网络中的另一台计算机通信的每台计算机只需要知道接收方的正确 IP 地址。这也是几乎所有家庭私人网络的基本设置:

正如我们刚刚学到的,对于正常的网络通信,所有参与者都需要在同一个网络中。如果网络只是这样,我们就必须在这里停下来,现代通信和全球网络就不会存在。事实上,全球连接了数百万个网络,例如我们自己的私人网络,它们都通过路由器连接在一起。如果您想与您网络中的另一台计算机或任何其他网络中的计算机进行通信,您的计算机需要具有所谓的 IP 路由表,该表定义了静态路由或通往特定目的地的下一跳。这个 IP 路由表是每个 Linux 操作系统的一部分。例如,如果我们有一个由三个子网络组成的私人网络,具有以下 IP 网络地址,如果您想与您子网络中的另一台计算机联系,您的路由表可能按以下方式工作。如果表中有一个条目,定义了如果有人想要访问10.0.2.0子网络的 IP 地址,例如,使用 IP10.0.2.15,则表中有一个路由条目,定义了您应该跳转到10.0.2.0网络:

如果您想访问具有 IP 地址192.168.122的机器,情况也是一样的。因为路由表中有一个条目,所以路由表将跳转到这台计算机所属的192.168.1.0网络:

对于没有明确规则的所有其他 IP 地址,将使用所谓的默认路由。在大多数私人网络中,默认规则是一个真实硬件路由器的 IP 地址,它基本上与 IP 路由表相同,但可以做更多的事情,因为它连接到全球其他路由器,它将找到通往正确目的地地址的路径:

这也被称为动态路由,因为源和目的地之间的路由器或路径可能会有所不同,取决于它将使用哪些路由器。通常,大多数互联网服务提供商提供的每个私人网络只有一个连接到公共互联网的公共 IP 地址:

我们私人网络中的所有计算机都需要通过这个路由器,带有其单个公共 IP 地址,如果它们想要与公共互联网中的其他计算机通信。

另一方面,如果来自互联网的外部公共机器想要访问我们子网络中的私人计算机,路由器需要处理将消息正确传递给正确的接收者,这些接收者只在我们的私人网络内部可见。

但是如何为计算机定义 IP 地址呢?IP 地址需要在与某个网络接口相关联的正确配置位置上设置在操作系统级别:

但是,如前所述,IP 地址在同一子网络中需要是唯一的;否则,无法找到网络消息的正确接收者。

那么,你如何处理呢?第一种方法是手动管理计算机列表和此网络中所有可用的空闲和保留 IP 地址。在这里,我们需要分配静态 IP 地址,这意味着每台计算机都会获得硬编码到系统中的 IP 地址,这些地址不会改变并保持稳定:

通常,网络中的重要服务,如邮件或 Web 服务器,具有静态 IP,因为它们必须始终在相同地址下从多台其他计算机或服务中可访问。但是,正如你可以想象的,这个系统非常不灵活,需要经常手动干预。想象一下一个公共无线热点,以及一直连接到该网络的所有人,使用多个设备,如智能手机、笔记本电脑和平板电脑。一个更好的解决方案是使用所谓的 DHCP 服务器。这是在您的网络中运行的一个服务,它监听新设备,并保持当前连接到网络的所有设备的数据库。它自动分配或撤销,并以非常可靠的方式管理 IP 地址给所有连接的机器:

分配给计算机的 IP 地址是动态的,这意味着明天,您的计算机可能具有不同于今天使用的 IP 地址。这个系统的好处是它还可以向连接的计算机发送有关您网络的其他信息,例如,您网络中私有 DNS 或邮件服务器的 IP 地址,如下图所示:

DNS 服务器是另一个非常重要的网络功能,我们需要了解。正如我们刚刚学到的,计算机只使用 IP 地址相互通信,这些只是数字。由于我们人类不擅长记忆或回忆长序列的数字,但在使用对象或事物的名称时要好得多,因此开发了一种系统,为这些 IP 地址分配名称或别名,以便我们可以用名称而不是 IP 地址来寻址计算机。

DNS 服务器有一个存储这些关系的数据库。由于计算机只能在网络上使用数字而不是名称,所以每次我们想要使用名称连接计算机时,都会内部询问相应的 DNS 服务器,将名称翻译为相应的 IP 地址,以便我们可以使用 IP 地址进行正确的连接。现在,为了解析正常互联网的名称,比如google.com,我们通常会使用一些由您的 ISP 或其他来源提供的公共 DNS 服务器:

但是,当公共 DNS 服务器没有此信息时,我们如何为子网络中的内部私有计算机的 IP 地址提供名称?一种解决方案是安装和设置我们自己的私有 DNS 服务器,并添加新的名称到 IP 地址关系。

由于这需要大量工作来安装和配置。一个更简单、更快速的解决方案是将名称与 IP 地址关系放在一个特殊的文件中,称为/etc/hosts文件:

使用 hosts 文件的最大缺点是,您必须在网络中的每台计算机上放置此文件,以解析网络名称,并且您还必须始终保持此文件的最新状态,以便每次向网络添加新计算机时,网络中的每台计算机都需要更新其 hosts 文件。

到目前为止,我们只谈到了 DNS 服务器和 hosts 文件的名称到 IP 地址关系。但是在这里,我们需要更详细地讨论这种名称的解剖。例如,您可以为网络中的所有计算机分配与其一起工作的人的主机名:

您也可以使用任何您喜欢的名称模式,但是,正如您可以想象的那样,这些主机名不够唯一,无法完全确定网络中的计算机,以便我们可以直接寻址。请记住,我们的私人网络可能由几个不同的子网络组成,例如,一个用于 IT 部门,一个用于人力资源部门:

在这里,两个人可以很容易地存在于不同的子网络中,所以这是行不通的,因为计算机主机名 Carl 存在于两个网络中,我们不能仅仅使用主机名来区分唯一的计算机名称。因此,我们还可以为子网络或网络地址分配名称。这种名称也称为 DNS 名称或域名。计算机的主机名或名称加上 DNS 名称,组合在一起,用点分隔,称为完全限定域名FQDN),每当我们需要访问本地子网络之外的不同网络中的计算机时,都是真正需要的:

因此,在这里,使用全名来寻址it-department.com的 Carl 将不会与human-resources.com子网络中的 Carl 发生冲突。

让我们回顾一下!主机名是计算机名称(例如,Carl),DNS 名称是网络或子网络的名称,例如my-company.comgoogle.com,完全限定域名是主机名加上由点分隔的 DNS 名称(例如,Carl.my-company.commail.google.com)。

在本书的第一章 Linux 简介 中,我们设置了三个名为Masterclient1client2的 VM。我们配置了我们三台机器的网络,每台机器都有一个网络接口,始终使用相同的隔离 IP10.0.2.15,这意味着三台虚拟机之间不能进行内部连接,因为它们都具有相同的 IP:

我们使用 VirtualBox 端口转发来通过主机端口222222232224访问我们的机器,这些端口都映射到机器的内部 SSH 端口22。现在,我们希望使机器能够使用内部私有网络进行通信。由于每个网络接口只能有一个 IP 地址,我们将通过为每台机器添加第二个虚拟网络适配器,并为每台机器添加来自另一个子网的新 IP 地址,从而实现这一目标,以便每个虚拟机都有一个网络适配器用于通过 SSH 进行公共访问,另一个用于内部子网通信:

如您所见,我们使用第二个子网10.0.5而不是我们的10.0.2作为我们的内部网络:

如果输入ip addr list,您将获得当前连接到计算机的所有网络接口的列表:

第一个设备是环回设备,这是一个非物理设备,以便我们可以与自己的计算机建立网络连接。它总是具有 IP 地址127.0.0.1。第二个网络接口是 enp0s3,这是 VirtualBox 配置提供的虚拟网络接口。这反映了以下设置中的一个:

这个虚拟网络接口的 IP 地址是10.0.2.15,主要用于我们可以通过 SSH 连接到机器。

现在,让我们给虚拟机添加另一个网络接口:

  1. 为了做到这一点,首先关闭机器:

  1. 现在,为了在虚拟机之间进行内部通信添加新的网络接口,以以下方式为每台机器添加第二个网络接口。首先,打开 VM 设置,转到网络,打开适配器 2 选项卡,启用它,并将其连接到内部网络。如您所见,内部网络的名称称为 internet。我们将把所有其他虚拟机放在同一个网络中:

  1. 现在,按“确定”继续。

  2. 对于您希望作为内部网络的一部分进行通信的每个虚拟机都要执行相同的操作。

  3. 现在让我们启动其中一个虚拟机来测试网络设置。

  4. 现在,如果您再次运行 IP 地址列表,您将看到我们新添加的网络接口出现在第 3 个接口上,名称为enp0s8。此外,您将看到当前没有自动为此设备分配 IP 地址:

  1. 让我们获取有关当前网络的一些信息。让我们显示我们网络设备的 IP 路由表:

如您所见,enp0s3网络适配器的 IP 地址为10.0.2.15,这是我们用来通过端口转发从主机机器连接的接口,目前在 IP 路由表中有两条路由。第一条路由是我们当前所属的子网10.0.2.0的路由。这意味着如果我们尝试联系我们子网中的另一台计算机,例如10.0.2.16,将采用这条路由。我们想要到达的所有其他 IP 地址都使用默认路由,指向 IP 地址10.0.2.2。这是我们路由器的 IP 地址。因此,例如,如果您想要访问 www.google.com,首先域名将使用 DNS 服务器转换为 IP 地址,然后将与我们的路由匹配。我们可以使用nslookup命令使用系统的默认 DNS 服务器将任何域名解析为 IP 地址。如您所见,google.com域名具有以下 IP 地址:

由于以172开头的 IP 地址不属于我们的子网,将使用默认路由。在10.0.2.2 IP 地址后面有一个真实的硬件路由器;它将负责虚拟机和google.com网站之间的正确路由。

在我们使用enp0s8网络接口在我们的三台虚拟机之间创建新的网络连接之前,让我们设置三个唯一的 FQDN。我们将使用 root 帐户进行此操作。

要打印出 FQDN,请使用hostnamectl status命令:

如您所见,目前我们有localhost.localdomain的 FQDN。现在,要更改 FQDN,请使用hostnamectl命令的 set-hostname 选项。在我们的示例中,我们使用了主机名或计算机名 master 和 DNS 名称centos7vt.com。完全合格的域名是master.centos7vt.com

让我们使用status选项进行重新检查。在我们的另外两个 VM 上,我们稍后将设置hostnames client1client2,以及相同的 DNS 名称centos7vt.com。您还可以通过编辑/etc/hostname文件来设置 FQDN。

要更改系统的默认 DNS 服务器 IP 地址,请打开名为/etc/resolv.conf的文件。在关键字名称服务器下,您可以更改或添加新的名称服务器。例如,要添加新的名称服务器,请引入新的名称服务器行并更改 IP 地址。在此示例中,我们将使用 Google 的官方 DNS 服务器地址,或者您可以只使用1

接下来,让我们为我们的新网络适配器enp0s8设置一个新的静态网络配置。在 CentOS 7 上,所有网络配置文件都可以在/etc/sysconfig/network-scripts中找到:

如您所见,对于enp0s3网络接口,有一个相应的网络接口配置文件称为ifcfg-enp0s3。通过键入cat ifcfg-eno0s3来查看其内容:

关于此以太网网络设备最重要的事情是,它从 DHCP 服务器获取其 IP 地址,设备在启动时激活,并且具有设备 IDenp0s3。在配置不同环境中的不同网络设备时,此配置文件中的其他项目也可能变得非常重要。由于if配置文件格式没有可视化手册页,请参考/usr/share/doc/initscripts-* sysconfig.txt中的出色文档。

如果打开文件并搜索ifcfg,您将找到解释ifcfg文件格式的所有不同项目的部分。例如,BOOTPROTO 项目可以具有值nonebootpdhcp。由于bootpdhcp都是指我们要将其配置为静态设备的新网络设备enp0s8的 DHCP 客户端,我们将使用BOOTPROTO none,但是我们在设置简单的静态网络连接时需要哪些项目呢?由于我们只设置内部网络,因此不需要设置任何路由,并且在我们的接口配置ifcfg)文件中只需要非常少的信息。

因此,我们将需要以下项目:名称、设备、IP 地址,因为我们将硬编码一个静态 IP 地址,以及 BOOTPROTO,我们将将其设置为 none。因此,让我们从介绍中审查我们的计划网络配置。

正如您所记得的,我们当前登录的主节点应该有第二个网络接口,静态 IP 地址为10.0.5.1Client1应该有第二个网络适配器,静态 IP 地址为10.0.5.2client2应该有10.0.5.3,所有这些都是用于节点之间的内部网络通信:

因此,让我们配置我们的新设备:

  1. 正如您所看到的,我们目前位于网络脚本文件夹,所有网络接口的配置文件都可以在这里找到。因此,让我们首先为我们的新网络接口创建一个新的配置文件:

  1. 我们将通过将现有的enp0s3网络设备的配置文件复制到新的enp0s8配置文件来简化我们的生活。现在让我们打开这个新的配置文件:

  1. 让我们将静态 IP 配置的引导协议更改为none。大多数项目都是不需要的,所以只需删除这些行。将设备名称更改为s8;这里不需要 UUID。还要更改设备 ID,将ONBOOT保留为yes,这样接口将在服务器重新启动时启动,并最后添加一行,定义我们静态互联网网络配置的硬编码 IP 地址。对于我们的主服务器,请使用 IP 地址10.0.5.1

  1. 现在保存文件并退出。

  2. 然后,我们需要强制重置我们的enp0s8网络接口,以便我们对配置文件所做的更改可以应用到设备上,并且静态 IP 地址可以生效。为此,首先使用ifdown命令关闭enp0s8设备。

  3. 然后使用ifup命令将其重新上线。

  4. 最后,让我们回顾一下ip addr列表命令。

如果您在重新启动设备之前和之后比较了enp0s8的输出,您将看到我们对配置文件所做的更改是有效的,现在我们的enp0s8网络设备具有静态 IP10.0.5.1

现在,在我们为 enp0s8 网络适配器设置了静态网络配置之后,让我们使用ip route show命令重新检查我们的 IP 路由表。如果您比较了我们设置新网络接口enp0s8之前和之后的路由表,您将看到为在我们的新10.0.0.0子网中路由网络通信创建了一个新路由。

作为主节点上仍然剩下的最后一件事,因为我们没有私有 DNS 服务器,需要在/etc/hosts文件中设置我们网络的计算机名称到 IP 的关系。始终通过首先使用完全合格的域名,然后可以添加更多的短主机名,从文件的末尾开始添加新条目。您可以始终为相同的 IP 地址添加多个名称:

第一个条目将是我们刚刚设置的自己的机器。其他条目是我们即将设置的客户端。保存并退出文件。现在启动两个客户端 VM。启动 VM 完成后,在您选择的终端模拟器中打开两个新标签。左侧的第一个标签保持与master节点的连接。在右侧的下一个标签上,请使用端口2223上的 SSH 端口转发登录到client2 VM。在第三个标签中,使用端口2224登录到client2 VM。现在转到我们打开的client1 VM 的中间标签。

在这里,让我们重复配置我们的enp0s8网络接口的步骤,以便我们可以在服务器之间建立连接:

  1. 首先,以 root 身份登录。

  2. 接下来,将完全合格的域名设置为client1.centos7vt.com

  3. 接下来,为我们的新enp0s8静态网络连接创建一个配置文件。在这里,输入与主机相同的信息;只需将 IP 地址更改为10.0.5.2。保存并退出文件。

  4. 接下来,重新启动网络接口:

正如您所看到的,我们已成功将10.0.5.2 IP 地址分配给了我们的 enp0s8 网络接口。最后,在/etc/hosts文件中添加条目,以便我们可以解析子网络中的其他域名。添加与主机相同的信息:

保存并退出文件。接下来,在第三个选项卡中对client2 VM 执行相同的步骤。首先以 root 用户登录,使用client2作为主机名,使用10.0.5.3作为 IP 地址,重新启动网络接口,最后,向/etc/hosts文件添加条目。

既然我们已经为通信设置了私有网络,测试它是否正常工作的最简单方法就是使用ping命令。该命令可用于查看另一个主机是否处于活动状态并且可达。如果不可达,将打印以下错误消息:

现在让我们从第一个选项卡中的master开始我们的连接测试。首先,让我们测试是否可以使用 IP 地址10.0.5.2连接到client1

如您所见,它有效。此外,测试一下我们是否可以使用 IP 地址10.0.5.3连接到client2

如您所见,这也有效。

作为下一步,测试我们的/etc/hosts配置是否也有效。为了这样做,让我们 ping 一下我们在该文件中设置的各种主机名。client1 的全限定域名有效。同样,主机名 client1 有效。C2 也作为 client2 的简称有效。client2 的全限定域名也有效。client2 的简称有效,c2 作为 client2 的非常简称也有效:

现在让我们转到 client1。在这里,让我们测试是否可以连接到主服务器:

是的,它有效。此外,您还可以使用不同的名称测试主服务器。让我们也测试一下 client2 的连接。测试不同名称下的主服务器,还要测试 client1。总之,我们可以说我们三台 VM 机器之间的网络配置现在正常工作。

安装新软件和更新系统

在本节中,我们将向您展示如何在计算机上安装新软件以及如何更新您的 CentOS 7 系统。

首先,让我们显示系统上当前安装的所有 RPM 软件包。键入yum list installed

在第一章的安装章节中,Linux 简介,我们已经演示了如何使用yum命令进行完整的系统更新,这将更新最小安装中已包含的所有 RPM 软件包,以及我们之后安装的所有软件包。

要获取系统上已安装的所有软件包的所有更新的列表,请键入以下命令查看新内容:yum check update

在这里,列出了所有的 RPM 软件包以及您可以安装的更新的新版本。所有的更新都必须使用 root 用户进行。所以首先以 root 用户登录。要仅更新单个 ROM 软件包,比如在可用软件更新列表中呈现的vim-minimal软件包,使用yum update,然后加入软件包的名称;例如,vim-minimal。在询问是否更新软件包时输入 yes,并再次输入yes确认导入 GBG 密钥:

正如我们所看到的,vim-minimal软件包已成功更新到最新版本。正如我们在第一章中已经学到的,在本书中,只需输入yum update来对系统上当前安装的所有软件包进行完整的系统更新。现在让我们按下N键来取消所有软件包的更新的下载和安装。大多数 yum 命令都需要用户的某种确认;例如,确认软件包的更新。如果你绝对确定会回答“是”任何问题,你可以通过提供-y标志来进一步自动化你选择的yum命令。这几乎适用于任何命令。这将执行你选择的 yum 操作,而无需用户进一步确认。

请注意,关于在更新软件包后是否需要重新启动系统存在着一个长期的争论。一般的共识是通常不需要,但是,如果内核或 glibc 软件包已经更新,你应该这样做。当然,出于安全原因,你确实应该这样做。

我们还可以看到,当我们比较系统中当前安装的内核和当前运行的内核时,需要重新启动:

当前运行的内核以514.el7结尾。当前安装的最新内核以514.21结尾,所以我们目前没有运行最新的内核。所以让我们重新启动系统。重新启动完成后,以 root 用户登录系统,再次输入uname -r命令,现在我们可以看到我们现在正在运行最新的内核,所以在这种情况下重新启动是必要的:

现在,要使用关键字(例如Apache2 Web Server)在你的软件包仓库中搜索,使用yum search命令,然后是关键字。这将打印出所有与关键字匹配的软件包的列表;在我们的例子中,apache,无论是在软件包名称还是在软件包描述中。

如果要获取有关某个软件包名称的更多信息(例如,HTTP 软件包名称),可以使用yum info子命令。

另一个非常有用的功能是,如果你知道一个 RPM 软件包中包含的文件或命令的名称,但实际上不知道这个命令或文件来自哪个 RPM 软件包,你可以使用yum whatprovides命令,将你正在搜索的命令或文件的前缀设为*/

在这个例子中,我们正在搜索所有包含ifconfig文件或命令的软件包名称。正如我们所看到的,我们在net-tools RPM 软件包中找到了一个匹配,其中存在一个二进制文件或命令/bin/ifconfig

现在,要安装一个软件包,使用yum install命令,提供软件包名称作为参数。在这个例子中,我们安装了 Apache HTTP 服务器软件包:

另一个有趣的命令是rpm -ql命令,后面跟上已安装软件包的名称,以获取该软件包安装的所有文件及其在文件系统中的确切位置的列表。要删除一个软件包,可以使用yum remove命令,然后是要删除的软件包的名称。

在第四章中,使用命令行,我们向您展示了如何使用第三方软件仓库epl来安装诸如htopiotop之类的软件,因为它们在官方 CentOS 7 软件仓库中不可用。例如,如果你搜索htop软件包,它在官方来源中是不可用的:

因此,让我们安装epl存储库,因为它可以从默认软件包源中获得。如您所见,可以使用epl-release RPM 软件包安装epl存储库:

使用以下命令查看epl存储库是否已成功安装,以检索系统中所有可用存储库的列表。

现在我们可以找到htop软件包,因为它是epl的一部分。安装其他存储库并不那么容易,因为官方来源没有 RPM 软件包,但大多数第三方存储库可以通过下载外部 RPM 来安装。您很可能会在网页上找到存储库。例如,对于著名的remi存储库,您可以首先从官方remi网站下载官方remi存储库 RPM 软件包:

接下来,使用带有大写Uvh选项的rpm命令安装下载的remi存储库 RPM:

然后,您需要通过编辑remi yum config文件来启用remi存储库。首先,在/etc/yum.repos.d文件夹中打开remi.repo文件。在这个文件中,转到remi部分,然后转到关键字enabled并将其从0更改为1

现在保存文件。然后,在更新软件包列表后,您可以使用新安装的第三方存储库。要重新检查第三方存储库是否已正确安装,还可以再次使用yum repolist命令:

服务简介

在本节中,我们将向您展示如何在 CentOS 7 中使用服务。

让我们在三个不同的选项卡中打开上一章节中的主服务器、client1 和 client2 三个 VM,它们连接在同一个内部子网络上。

让我们从安装一个简单的网络服务开始。在我们的示例中,让我们在主服务器上安装 Apache2 Web 服务器,因为它非常容易设置和使用:

现在,在 CentOS 7 上安装httpd软件包后,您可以使用systemctl命令来管理服务,该命令是systemd服务的一部分。

要获取系统中当前可用的所有单元的列表,请使用以下命令:system ctl list-units。这将打开带有较少导航的单元列表:

如您所见,有不同类型的单元文件可用;例如,以device结尾的文件,以mount结尾的文件和服务文件。按q退出导航。要获取系统中当前可用的所有服务的列表,只需键入systemctl list-unit-files,然后使用--type=service进行服务过滤。在此列表中,您将看到系统中当前启用或禁用的所有可用服务。与我们安装的 Apache2 Web 服务器一样,当前已禁用的httpd 服务文件也存在。要获取单个服务的详细状态,请使用带有status选项和服务名称的systemctl命令;在我们的示例中,是httpd服务:

如您所见,在安装新的 Apache HTTP 服务器后,服务未运行。默认情况下,systemd服务可以有两种不同的状态对我们很重要:已启用或已禁用,已激活或未激活。在我们的示例中,httpd服务在安装后默认处于禁用和未激活状态。与任何其他服务一样,Apache HTTP 服务器默认情况下是禁用和未激活的。已启用意味着服务应在每次启动 Linux 系统时自动启动,这也称为启动时。已激活意味着服务当前正在运行。

要启动一个服务,请使用systemctl start选项,然后是服务的名称;在我们的例子中是httpd.service。现在再次检查服务,再次使用status选项:

正如你所看到的,它现在正在运行。此外,你还可以在这里的输出中看到另外两个非常重要的事情。首先,你可以看到一个服务可以由多个进程组成。在我们的例子中,httpd 服务由六个不同的 HTTP 进程组成。另一个重要的事情是,systemctl status命令在启动服务时会输出服务生成的最后几行消息。进程生成的这些有用的文本行也称为日志,可以为我们提供有关服务运行行为的有用信息。不过,我们的服务目前是禁用状态。要启用它,请使用systemctl enable选项。现在再次查看状态:

现在你可以看到它也已经启用,所以这项服务将在每次重新启动服务器时自动启动。要停止当前运行的服务,请使用systemctl stop选项。我们会看到它再次变为非活动状态。

需要注意的是,启动或停止不会影响服务的禁用或启用服务器引导行为。在这里,这项服务仍然是启用的,尽管它没有运行。反之亦然。禁用或启用服务不会启动或停止它。

要禁用一个服务,请使用systemctl disable选项。然后再次启动服务。现在,为了测试我们的 HTTP 服务器是否正常工作并且能够托管和传递 Web 内容,让我们首先为我们的服务器创建一个标准主页。我们服务器的标准主页是/var/www/html文件夹中的index.html文件。现在,将以下 HTML 内容包含进去,这是我们服务器的问候消息:

保存并退出文件。现在,要从我们所在的主服务器上的新 Web 服务器访问我们的主页,请使用wget

如你所见,我们可以从我们的主服务器本地正确访问主页。现在,如果你停止 Web 服务并尝试再次访问我们的网页会发生什么?你会发现网页不再可访问。重新启动 Web 服务器。现在,让我们测试一下我们是否可以从本地网络中的另一台计算机访问我们的新 Web 服务器。只需转到 client1 选项卡并测试 Web 服务器是否可以通过网络访问。你会发现它是不可以的。

基本系统故障排除和防火墙

在本节中,我们将继续上一节中开始的 Apache2 Web 服务器的工作,以便使其可以被我们子网络中的其他计算机访问。此外,我们还将简要介绍 CentOS 7 中的 Linux 防火墙。

本章的第一部分简要提到,网络连接总是通过 IP 地址和端口的组合进行的,这两者合称为套接字地址。现在,每个 Linux 网络服务,比如邮件或 Web 服务器,都必须连接到 IP 地址和端口,这样我们才能从网络中的另一台计算机或同一台本地计算机上建立连接:

当我们谈论网络通信时,我们经常将其称为“一个服务正在监听 IP 地址 a 端口 b”。例如,我们的 Web 服务器正在监听 IP 地址10.0.2.15的端口80,邮件服务正在监听端口24,Web 服务正在监听 IP 地址10.0.2.15的端口80,FTP 服务正在监听 IP 地址10.0.2.15的端口21

但也许你想知道,如果我们在系统上配置了多个网络接口,每个接口都有不同的 IP 地址,那么服务使用哪个 IP 地址进行通信?答案很简单。在安装后,任何 Linux 系统上的大多数网络服务默认会监听所有可用的网络接口进行网络连接。对于几乎所有标准服务,你也可以将其更改为仅监听特定的网络接口、网络连接或子网络,甚至是网络范围:

有些甚至在安装后默认只监听本地主机,因为这些通常是非常关键的服务,系统管理员需要有意更改监听地址,以此作为对风险的责任意识。

假设你有一台运行多个网络服务并且每个服务都在不同端口上监听的 Linux 服务器。防火墙是管理计算机连接的工具。在 Linux 中,标准防火墙称为firewalld。这个防火墙可以保护你的系统免受外部不需要的网络连接,例如,如果有人试图侵入你的系统并窃取数据。它通过管理你的入站网络端口来实现。默认情况下,firewalld关闭除了用于 SSH 连接的端口22之外的所有入站网络端口。否则,你将无法远程连接到你的机器:

因此,如果你想进行某种网络通信,你必须明确告诉防火墙这样做。你可以打开或关闭单个端口或端口范围等。这在管理服务器安全性方面非常有帮助,但重要的是要注意,默认情况下,firewalld 不会限制系统内的任何本地网络通信,因此本地主机网络连接始终有效,并且不会被防火墙阻止。此外,非常重要的是要知道,默认情况下,firewalld 只是一个入站防火墙,这意味着它根本不会阻止任何出站连接:

为了解决这个问题,我们需要知道如何对系统服务进行故障排除。因此,首先回到我们的主服务器,查看运行此 Web 服务器的情况。要找出服务是否出现问题,至少有三个地方可以查看。我们应该做的第一件事是检查systemctl status的输出,就像我们之前做的那样。正如你所看到的,服务目前正在运行,服务的最终当前输出行也显示为OK

有时,在这里的输出中,如果服务没有正常运行,你会找到错误消息或警告。

有时,服务的日志输出的最后两行不足以,因此如果你需要对服务进行故障排除,可以查看的第二个地方是journalctl命令。如果你使用journalctl命令和-u标志,你可以过滤你选择的服务的日志消息;在我们的示例中,是httpd服务:

在我们的示例中,在journald中找不到任何可疑的日志输出,这是将所有正在运行的服务的所有日志消息写入集中数据库的服务。Apache HTTP 服务器的日志看起来很正常。

因此,我们可以查看故障排除服务的第三个地方是rsyslog日志文件,它位于/var/log/messages。打开这个文件,按大写G键跳到末尾。

在这里,rsyslog文件中并没有记录任何可疑的内容。

一些服务,比如我们的 Apache HTTP Web 服务器,提供了自己的日志文件,用于故障排除或获取有关服务的信息。

请注意,没有标准化的目录可以让服务输出自己的日志文件,但是一些服务会将它们的日志文件写入到/var/log文件目录下的子目录中。在这里,您可以找到两个日志文件。一个是access_log,记录用户访问我们的 Web 服务器(例如,已下载的服务器上的文件)。另一个是error_log文件,记录此服务可能遇到的各种错误。因此,首先查看access_log文件:

这看起来非常正常。现在,也打开error_log文件。使用大写G跳转到末尾:

在这里,找不到特殊的错误消息。

解决 CentOS 7 上的 Apache HTTP Web 服务器之外没有人能访问的问题是因为有一个非常严格的防火墙正在阻止几乎所有传入的网络连接。

您可以通过输入firewall-cmd --list-all来查看当前允许的防火墙规则。在 CentOS 7 上,标准防火墙称为 firewalld:

如您在这里所见,只有 SSH 服务默认允许与我们的服务器通信。Firewalld 主要保护所有传入的网络连接。从我们的服务器到其他服务器的出站连接没有受到限制或限制;这就是为什么我们可以从本地主机访问我们的 Web 服务器,但不能从任何其他主机访问的原因。

为了解决这个问题,我们可以在防火墙中打开 HTTP 服务,也就是打开端口 80。为了能够永久地这样做,使用以下两个命令:firewall-cmd --permanent --add-service,然后是http。为了应用更改,接下来重新加载防火墙规则。最后,让我们看看 HTTP 服务现在是否在防火墙中启用了:

如您所见,它有效。

最后,让我们测试一下是否可以从另一台服务器远程连接到我们的 Apache Web 服务器。转到 client1 并重复wget命令:

是的,它有效!您现在可以在您的网络中访问您的 Web 服务器。

到目前为止,我们还没有讨论如何从防火墙中删除服务。要从防火墙配置中删除 HTTP 服务或端口,请使用以下防火墙命令语法,firewall-cmd --permanent --remove-service

然后选择服务;在我们的例子中,是http服务。与添加服务类似,您还必须在这里重新加载防火墙。让我们重新检查一下防火墙设置:

如您所见,HTTP 端口已关闭。

最后,firewalld 服务的一个非常有用的功能是可以打开单独的端口号,而无需提供服务名称。如果需要打开一个没有服务文件(例如 HTTP)的端口,这将非常有用。例如,要打开端口12345,请使用 TCP 协议。让我们在重新加载防火墙后显示新的防火墙配置:

如您所见,端口12345现在使用 TCP 协议打开。除了 TCP,您还可以使用 UDP 协议。现在,要使用 TCP 协议关闭端口12345,请使用以下命令。在这里,还要重新加载防火墙配置。让我们进行重新检查:

让我们总结一下到目前为止我们学到的东西:

  1. 如果是与服务相关的问题,首先查看服务的systemctl输出。

  2. 如果问题仍然存在,请查看服务的journalctl输出。

  3. 如果这是一个一般的系统问题,或者您无法通过systemctljournalctl的输出来解决您的服务问题,接下来看一下/var/log-messages rsyslog输出文件。

  4. 此外,一些服务提供了journaldrsyslog文件之外的特殊日志文件位置,因此也要查看那里。但您必须意识到,并非每个服务或程序都有这样一个特殊的日志文件目录或输出。

  5. 最后,我们向您简要介绍了使用预定义服务文件(如 HTTP)的 firewalld 服务,并向您展示了如何处理未由服务文件定义的单个端口。在下一章中,我们将向您展示高级文件权限。

介绍 ACL

在本节中,我们将向您简要介绍 ACL(访问控制列表)的工作原理。

Linux 具有一些特殊的文件和文件夹权限,即 ACL(访问控制列表)、setuidsetgidsticky bit。如果您查看文件系统中的文件,比如只有 root 用户可以访问的新文件,当前我们登录为olip

如您所见,olip用户对该文件没有写访问权限。也许您已经问过自己这个问题:如何将文件或文件夹的权限授予不是文件或组所有者的个别用户,比如我们的例子中的 root 用户?唯一的方法是使用其他组,但这不是个别的,因为所有不是文件或组所有者的用户都属于此类别。但在这里,我们想要设置单个用户权限;例如,对于olip用户。

ACL(访问控制列表)是一个系统,它通过简单的所有权和权限模型扩展了我们在 Linux 下的正常文件访问控制。使用 ACL,您可以在单个用户或组级别上定义文件或文件夹权限。要使用 ACL,请使用getfaclsetfacl命令。

例如,要显示 ACL,请使用getfacl命令,然后是要显示权限的文件名:

如您所见,目前此文件上没有设置 ACL。与普通文件权限一样,如果我们想要更改某些内容,就需要以 root 身份登录。现在,例如,要为olip用户设置 ACL,请使用以下命令。如果您还记得第三章 Linux 文件系统,这应该是不言自明的:

要显示 ACL,请再次查看此文件的 ACL。如果比较之前和之后的getfacl命令输出,您将看到我们现在为olup用户设置了单个用户权限:执行。现在,olip用户应该能够写入此文件:

成功;ACL 正常工作。

您还可以基于组设置 ACL。在这里,我们将使用组标识符而不是用户。要删除单个 ACL,请使用-x标志。您还可以通过ls -l命令的输出中标记的加号来查看文件是否设置了 ACL:

setuid、setgid 和 sticky bit

在本节中,我们将向您展示有关特殊文件权限标志setidsetgidsticky bit的所有必要信息。

setuid

现在让我们谈谈setuidsetgidsticky bit。在处理用户、组和文件权限时,让我们首先以 root 身份登录。

首先,让我们创建一个新用户、组,并在本地复制whoami命令,以查看setuid标志的情况:

接下来,让我们将此命令的文件所有者和组所有者更改为awesome_userawesome_group

使用八进制表示法也可以设置setuidsetgidsticky bit。您已经从文件权限章节了解了它们。这些特殊权限可以通过文件权限字符串中的一个额外位来表示,使用以下代码:

setuid的数字是4setgid的数字是2粘滞位的数字是1。与文件的简单执行权限类似,您还可以向文件添加特殊权限的组合:

如果要设置setuidsetgid标志,您需要将42相加,总共是6,或者setgid粘滞位3表示,或者粘滞位setuid5表示。

现在,我们如何设置特殊权限信息?可以使用chmod命令中的附加数字进行设置。您已经知道,为用户、组和其他人定义权限需要三个数字。要在文件上显示特殊权限,可以使用ls -l命令,但这很难阅读,更容易使用getfacl命令,它不仅适用于 ACL,还显示了我们特殊权限的标志。默认情况下,没有为任何文件定义标志或特殊权限,如您在getfacl命令的输出中所见:

现在,要向文件添加特殊权限标志,或者换句话说,设置setuidsetgid粘滞位,您可以使用带有四个数字的chmod命令,而不是三个数字,其中第一个前导数字定义了特殊权限。例如,如果您在chmod命令的第一个位置使用2,则会设置设置组 ID 标志,该标志显示在标志行中。如果我们在第二个位置有s,那么它就是设置组 ID:

现在,要设置setuid标志,请在chmod命令中使用数字4作为第一个数字。使用getfacl命令重新检查。在标志行中,最左边的字符已设置为s

现在,添加特殊文件权限标志的组合(例如,数字 6,表示setuidsetgid的组合,或 4 加 2 等于 6),在getfacl输出中以以下方式显示:

最左边的第一个标志是setuid标志,第二个标志是setgid标志。要设置所有三种权限类型,setuidsetgid粘滞位,请使用getfacl(路径):

在这里,您可以看到所有三个标志都已设置。粘滞位的短标志是t,而不是s

要删除所有特殊文件权限,只需将0用作文件权限编码的数字,并将0用作chmod命令的第一个数字:

现在,让我们简要讨论一下setuid权限。setuid标志仅对可执行命令重要,对目录或其他文件类型无关紧要。还要知道,出于安全原因,它不适用于脚本文件,而仅适用于编译的二进制可执行文件。

如前所述,每个进程都有一个关联的用户,我们称之为“用户运行命令”。在这个例子中,您看到的所有进程都是由 root 用户运行的:

现在,setuid权限标志将以定义为该文件所有者的用户身份运行命令。这对于系统中的一些特殊命令非常重要和有用;例如,必须作为 root 用户运行的命令,因为它们访问受保护的文件系统文件或文件夹,但对于普通用户也必须是可执行的。例如,passwd命令。它访问并写入文件,例如etc/passwd文件,该文件仅对 root 可写,因此必须以 root 身份运行此命令,但普通用户也需要在passwd命令上更改密码:

现在,让我们退出 root 用户,用普通用户账户测试setuid标志。

让我们再次检查一下我们是否真的是olip用户。现在,如果不在文件上设置setuid标志,如果我们执行本地的whoami命令,它将打印出我们的用户名,因为我们是启动它的用户:

现在,如果我们在该命令上设置setuid权限并再次执行它,会发生什么?首先,让我们查看权限标志。我们会看到该文件上成功设置了setuid标志。现在,让我们再次执行一个命令:

正如你所看到的,setuid标志的效果如预期。我们以olip用户身份运行命令,但在执行过程中使用的是文件所有者awesome_user

setgid

现在,让我们了解一下setgid权限。这个标志有两个不同的含义,这是很重要的,应该被记住。当在文件上设置时,它与setuid权限具有相同的效果,但这里它将以组所有者的权限执行命令,而不是文件所有者的权限。

chmod命令中使用数字2来设置文件的setgid标志:

setgid标志的第二个含义非常重要,应该被记住,因为它可能是一个典型的用例。如果在文件夹而不是文件上设置了setgid,那么在该文件夹中创建的每个新文件、文件夹或子文件夹都将自动获得设置了setgid标志的文件夹的组权限。这适用于递归包含的所有文件。这可能非常重要,因为通常新创建的文件的组权限会自动由文件的创建者分配。

因此,如果你想要在文件系统中为协作或团队工作分隔位置,可以将共享文件放入其中,供属于特定组的任何人使用,setgid是一个非常强大的功能。这就像你可能从其他操作系统中了解到的共享文件夹。因此,如果你想要将文件系统分隔成为协作或团队工作的位置,任何属于特定组的人都可以创建文件,并且其他同一组的人可以完全访问这些文件,只需在文件夹上设置setgid标志。

为了测试这个:

  1. 在用户名olip下创建一个新文件夹。

  2. 现在,将组所有权更改为awesome_group。现在,如果用户在该文件夹中创建新文件,它将具有该用户的组所有权。

  3. 现在,让我们在那个文件夹上设置setgid标志,看看会发生什么。

  4. 让我们在设置了setgid标志的文件夹中,使用用户名olip创建一个新文件:

正如你所看到的,现在在这个文件夹中创建的任何新文件都会获得文件夹的组所有权,即awesome_group。所以我们的setgid标志正常工作。

粘性位

sticky bit只对目录有效,对文件无效。如果在文件夹上设置了sticky bit,则只有特定文件、文件夹或子文件夹的所有者才能删除它。有一些特殊情况下这是有用的,例如在/tmp目录中,任何人都应该被允许查看任何内容,但很多时候进程会在该文件夹中创建并依赖存储的数据,因此如果除了进程的创建者之外的其他人能够删除其他用户的文件,那将是非常糟糕的。

让我们来测试一下:

如您所见,“粘性位”已在/tmp目录上设置,因此让我们在/tmp目录中使用olip用户创建一个新文件。现在,让我们用awesome_user登录。由于尚未设置密码,让我们为其设置一个密码。现在,awesome_user也将在/tmp目录中创建一个新文件。现在,让我们尝试删除我们自己的文件,这是有效的。现在,让我们尝试删除olip用户的文件;这不起作用,因此“粘性位”正在按预期工作:

摘要

在本章中,我们向您简要介绍了 Linux 中特殊文件权限标志。setuid标志仅适用于命令,而不适用于脚本,并允许程序以文件所有者定义的用户而不是运行该程序的用户来执行。setgid标志有两个特殊含义。第一个是用于命令,另一个是用于文件夹。如果您在命令上设置它,它将像setuid标志一样工作,但将以该文件的组所有权而不是该文件的文件所有者来运行它。第二个含义是,如果您在文件夹上设置它,您设置了setgid的文件夹的组所有者将自动分配给您在该文件夹中创建的每个新文件。在设置了“粘性位”的目录中,只有文件所有者才能删除自己的文件。

posted @ 2024-05-16 19:08  绝不原创的飞龙  阅读(17)  评论(0编辑  收藏  举报