Linux-快速学习手册-全-

Linux 快速学习手册(全)

原文:zh.annas-archive.org/md5/d44a95bd11f73f80156880d7ba808e3a

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Linux 在 IT 行业中需求量巨大,因为它为全球超过 90%的超级计算机和服务器提供动力。Linux 也是公共云中最受欢迎的操作系统。Linux 是全球顶级公司的基础架构,如亚马逊、谷歌、IBM 和 Paypal。您现在就需要开始学习 Linux!快速学习 Linux,第一版是在两年的时间内编写的,从 2018 年 5 月到 2020 年 5 月。这本书采用了一种现代的学习 Linux 的方法,您一定会欣赏它的独特性和友好的语气。

第一章:这本书是为谁准备的

如果您一直想学习 Linux,但仍然害怕这样做,那么这本书就是为您准备的!很多人认为 Linux 是一种只有黑客和极客才知道如何使用的复杂操作系统,因此他们放弃了学习 Linux 的梦想。好吧,让我来给您一个惊喜!Linux 是简单易学的,而这本书就是最好的证明!您可能已经看过各种解释 Linux 的来源,它们都以复杂和枯燥的方式解释 Linux。而这本书恰恰相反;它以一种愉快和友好的方式教会您 Linux,这样您永远不会感到无聊,而且您总是会有动力学习更多。快速学习 Linux不假设任何先前的 Linux 知识,这使它非常适合初学者。然而,中级和高级的 Linux 用户仍然会发现这本书非常有用,因为它涵盖了广泛的主题。

这本书涵盖了什么内容

第一章,您的第一个按键。在这个介绍性的章节中,您将了解 Linux 的历史以及 Linux 在当今世界的影响以及它可能如何塑造未来。您还将学习如何安装 Linux 虚拟机并运行一些简单的命令。

第二章,攀登树。在本章中,您将学习 Linux 文件系统层次结构的组织方式,并探索各种 Linux 命令,这些命令将帮助您浏览 Linux 目录树。

第三章,遇见编辑器。在 Linux 上,您所做的大部分工作都与文件有关!在本章中,您将学习如何使用流行的文本编辑器,如nanovi来查看和编辑 Linux 文件。您还将学习一些方便的命令,让您可以在自己的终端舒适地查看文件!

第四章,复制、移动和删除文件。在本章中,您将学习如何对文件执行各种操作。您将学习如何复制、移动和删除文件。您还将学习如何重命名和隐藏文件!

第五章,阅读您的手册!让我们诚实一点!您无法记住所有存在的 Linux 命令;没有人可以!这就是为什么在本章中,您将学习如何利用和使用各种 Linux 帮助和文档工具。

第六章,硬链接与软链接。在本章中,您将首先了解文件inode的概念。您还将学习如何创建硬链接和软链接,以及它们之间的区别。

第七章,谁是 Root?是时候终于见到 root 用户了!在本章中,您将了解普通用户的限制,还将意识到 root 用户有多么强大;您还将学习如何在系统上切换不同的用户。

第八章,控制人口。您可以把 Linux 想象成一个强大的大国!在本章中,您将学习如何在 Linux 中引入各种用户和组。您将学习如何修改用户和组属性。您还将学习如何更改文件权限和所有权。

第九章,管道和 I/O 重定向。在这一章中,您将学习如何使用 Linux 管道将一个命令的输出发送到另一个命令的输入,从而实现更复杂的任务。您还将学习如何进行输入和输出重定向。

第十章,分析和操作文件。在这一章中,您将探索一系列 Linux 命令,这些命令将帮助您分析和操作文件。您将学习如何查看文件之间的差异,显示行数,查看文件大小等等!

第十一章,让我们玩寻找和寻找。不知道文件在哪里?别担心!在这一章中,您将学习如何使用 locate 和 find 命令在 Linux 系统上搜索文件。

第十二章,你得到了一个软件包。在这一章中,您将学习如何在 Linux 系统上安装、删除、搜索和更新软件。您将了解 Linux 中使用的软件术语,包括软件包仓库软件包管理系统

第十三章,终结进程。在这一章中,您将学习如何与 Linux 进程交互。您将了解子进程和父进程之间的区别。您还将了解如何在后台运行进程。此外,您还将学习如何终止进程!

第十四章,sudo 的力量。在这一章中,您将学习如何授予用户和组sudo访问权限,以便他们可以执行管理任务。您将学习如何使用visudo命令编辑sudoers文件,并学习添加sudo规则的正确语法。

第十五章,网络出了什么问题?您的网络出了问题!在这一章中,您将学习如何排除网络连接问题。您将学习如何查看您的 IP 地址、DNS、网关和主机配置。此外,您还将学习如何重新启动您的网络接口。

第十六章,Bash 脚本编程很有趣。在这一章中,您将学习如何创建 bash 脚本。您将学习如何使用条件语句为您的 bash 脚本增加智能。此外,您还将学习如何循环和创建 bash 函数。

第十七章,你需要一个定时任务。不想全天候被电脑绑定?cron任务可以帮到你!在这一章中,您将学习如何使用cron任务安排任务。您还将学习如何使用at实用程序安排一次性任务。

第十八章,归档和压缩文件。在这一章中,您将学习如何将文件组合成一个归档文件。您还将学习如何使用各种压缩工具压缩您的归档文件,节省一些磁盘空间。

第十九章,创建您自己的命令。您想定义自己的 Linux 命令吗?在这一章中,您将学习如何使用别名来创建自己的 Linux 命令。您还将学习如何创建临时和永久别名。

第二十章,每个人都需要磁盘空间。在这一章中,您将学习如何分区硬盘。您还将学习如何创建和挂载文件系统。此外,您还将学习如何修复损坏的文件系统。此外,您还将学习如何使用 Linux LVM 创建逻辑卷。

第二十一章,echo“再见,我的朋友”。您的下一步可能是什么?让我给您一些建议,告诉您在阅读本书后该做些什么。

要充分利用本书

这本书的唯一要求基本上是任何能工作的计算机!

本书涵盖的软件/硬件 操作系统要求
任何虚拟化软件,如 VirtualBox,VMware Player 或 VMware Fusion Windows,macOS 或 Linux

如果你正在使用本书的数字版本,我们建议你自己输入命令和脚本。这样做将帮助你避免与复制和粘贴命令和脚本相关的任何潜在错误。

我非常相信“熟能生巧”的原则。你练习 Linux 的次数越多,你就会越熟悉它。你可以将 Linux 安装为你的主要操作系统;这样你就可以每天都使用 Linux。如果这对你来说不是一个选择,那为什么不买一个便宜的树莓派来玩玩呢?

下载彩色图像

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

使用的约定

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

CodeInText:表示文本中的代码词,数据库表名,文件夹名,文件名,文件扩展名,路径名,虚拟 URL,用户输入和 Twitter 句柄。例如:"exitcd命令是 shell 内置命令的两个例子。"

当我们希望引起你对代码块的特定部分的注意时,相关行或项目将以粗体显示:

[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)

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

$ mkdir css
$ cd css

粗体:表示一个新术语,一个重要的词,或者屏幕上看到的词。例如:"文件名是 inode 数据结构的一部分。"

警告或重要提示会以这种形式出现。

技巧和窍门会以这种形式出现。

联系方式

我们非常欢迎读者的反馈。

一般反馈:如果你对本书的任何方面有疑问,请在你的消息主题中提到书名,并发送邮件至customercare@packtpub.com

勘误:尽管我们已经非常注意确保内容的准确性,但错误是难免的。如果你在这本书中发现了错误,我们将不胜感激地接受你的报告。请访问www.packtpub.com/support/errata,选择你的书,点击勘误提交表格链接,并输入详细信息。

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

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

评论

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

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

你的第一个按键

我想欢迎你来到这本书的第一章。当你阅读这本书时,你会感觉自己在读一个故事,但这不是一般的故事,这是 Linux 的故事。在这一章中,你将了解 Linux 的起源以及 Linux 对当今世界的影响。你还将了解 Linux 如何塑造计算机的未来。最后,你将学会如何在计算机上安装 Linux 虚拟机。所以,不多说了,让我们开始吧。

第二章:一点历史

Linux 的故事始于 1991 年,当时芬兰赫尔辛基大学的计算机科学学生 Linus Torvalds 开始写一个免费的操作系统作为业余爱好!现在想想,他的业余项目竟然成为了历史上最大的开源项目。哦,如果你还没有意识到,这个免费的操作系统就是 Linux。网上有很多关于开源的定义,其中一些对于没有经验的读者来说有些混乱,所以这里有一个简化的解释:

什么是开源?

开源项目是指其源代码对公众开放查看和编辑的软件项目。

源代码只是用于开发软件的代码(程序)的集合;在 Linux 的上下文中,它指的是构建 Linux 操作系统的编程代码。现在你知道什么是开源,很容易想象什么是封闭源:

什么是封闭源?

封闭源项目是指其源代码不对公众开放查看和编辑的软件项目。

Linux 是开源项目的最著名的例子。另一方面,微软 Windows 是封闭源项目的最著名的例子。

有些人不知道什么是操作系统,但不用担心;我会帮你解释。这里有一个简单的操作系统定义:

什么是操作系统?

操作系统是一种管理计算机资源(如内存和磁盘空间)的软件程序。它还允许计算机的硬件和软件相互通信。操作系统也可能包括其他应用程序:文本编辑器、文件管理器、图形用户界面、软件管理器等。

这里有很多不同的操作系统;以下是一些例子:

  • Linux

  • Android

  • macOS

  • Microsoft Windows

  • Apple iOS

  • BlackBerry

请记住,这个列表非常简短,无法全面涵盖。有大量的操作系统,甚至很难统计它们的数量。

谈到操作系统时,我们必须提到内核,它是任何操作系统的核心。

什么是内核?

内核只是任何操作系统的核心。它是操作系统的一部分,用于组织对 CPU、内存和磁盘等系统资源的访问。

请注意,在定义中,我说内核是操作系统的一部分。下图可以帮助你形象地理解内核和操作系统之间的区别。

图 1:操作系统 vs. 内核

与微软 Windows 或 macOS 不同,Linux 有许多不同的风味;这些风味被称为发行版,也被简称为 distros。

什么是 Linux 发行版?

由于 Linux 是开源的,许多人和组织已经修改了 Linux 内核以及 Linux 操作系统的其他组件,以开发和定制适合他们需求的 Linux 版本。

实际上有数百种 Linux 发行版!你可以去www.distrowatch.com查看庞大的 Linux 发行版列表。

distrowatch.com的好处在于它显示了世界上所有 Linux 发行版的受欢迎程度排名。您甚至会发现一些 Linux 发行版是根据特定目的设计的。例如,Scientific Linux 是许多科学家中受欢迎的 Linux 发行版,因为它预装了许多科学应用程序,这使它成为科学界的首选 Linux。

今天的 Linux 和未来

1991 年,Linux 只是一个小宝宝。但这个宝宝迅速成长,变得非常受欢迎。如今,Linux 为全球超过 90%的顶级超级计算机提供动力。而且让你惊讶的是,你可能已经使用 Linux 多年而没有注意到。怎么可能?如果你曾经使用过安卓智能手机,那么你就使用过 Linux,因为安卓是一个 Linux 发行版!如果你还不相信我,去distrowatch.com搜索安卓。

更严肃的问题是,大多数政府服务器都运行 Linux,这就是为什么您会看到很多政府技术工作需要懂得 Linux 的人。此外,亚马逊、eBay、PayPal、沃尔玛等大公司都依赖 Linux 来运行他们的先进和复杂的应用程序。此外,Linux 在云端占据主导地位,超过 75%的云解决方案都在运行 Linux。

Linux 的故事真是鼓舞人心。曾经的爱好现在已经成为互联网的主导力量,而 Linux 的未来看起来更加光明。著名的汽车制造商和汽车制造商如雷克萨斯和丰田现在正在采用 Linux 技术,比如汽车级 LinuxAGL)。您可以在www.automotivelinux.org上找到更多信息。

Linux 还运行在许多嵌入式设备上,并且是流行的树莓派、Beagle Bone 和许多其他微控制器的基础。您甚至可能会惊讶地知道一些洗衣机也在运行 Linux!所以每当你去洗衣服的时候,花点时间,感谢我们生活中有 Linux。

安装 Linux 虚拟机

有多种安装 Linux 系统的方法。例如,如果您目前正在作为主要操作系统运行 Windows,那么您可能可以在 Windows 旁边双启动 Linux,但这种方法对初学者不友好。安装过程中的任何错误可能会给您带来很多头痛,而且在某些情况下,您甚至可能无法再启动 Windows!我想要帮您避免很多痛苦和烦恼,所以我将向您展示如何将 Linux 安装为虚拟机。

什么是虚拟机?

虚拟机就是在另一台计算机(主机)内运行的计算机。虚拟机共享主机资源,表现得就像独立的物理机一样。

您还可以拥有嵌套虚拟机,这意味着您可以在另一个虚拟机内运行虚拟机。

安装虚拟机的过程很简单,您只需要按照以下步骤进行:

  1. 安装 VirtualBox(或 VMware Player)。

  2. 下载任何 Linux 发行版的 ISO 镜像。

  3. 打开 VirtualBox 并开始安装过程。

第一步是安装 VirtualBox,这是一个跨平台的虚拟化应用程序,可以让我们创建虚拟机。VirtualBox 是免费的,可以在 macOS、Windows 和 Linux 上运行。快速搜索一下:VirtualBox 下载就可以搞定。如果你感到有点懒,你可以在以下链接下载 VirtualBox:www.virtualbox.org/wiki/Downloads

安装完 VirtualBox 后,您现在需要下载任何 Linux 发行版的 ISO 镜像。在本书中,您将使用 Ubuntu,这在初学者中可能是最受欢迎的 Linux 发行版。您可以在以下链接下载 Ubuntu:www.ubuntu.com/download/desktop

我建议您下载最新的 Ubuntu LTS长期支持)版本,因为它经过了充分测试,并且有更好的支持。

最后一步,您需要打开 VirtualBox,并使用您从步骤 2下载的 Ubuntu ISO 镜像创建一个 Linux 虚拟机。

打开 VirtualBox 时,您需要从菜单栏中选择“新建”。

图 2:创建新虚拟机

然后,您需要选择新虚拟机的名称和类型。

图 3:选择名称和类型

之后,点击“继续”,选择要为虚拟机分配多少内存。我强烈建议选择2GB 或更多。例如,在下面的截图中,我选择为我的虚拟机分配4096MB 的内存(RAM),相当于4GB。

图 4:选择内存大小

之后,点击“继续”,确保选择“现在创建虚拟硬盘”,如下截图所示,然后点击“创建”。

图 5:创建硬盘

之后,选择VDIVirtualBox 磁盘映像),如下截图所示,然后点击继续。

图 6:硬盘文件类型

现在选择“动态分配”,如下截图所示,然后点击“继续”。

图 7:物理硬盘上的存储

现在您可以选择虚拟机的硬盘大小。我强烈建议您选择10GB 或更高。在下面的截图中,我选择了20GB 作为我的虚拟机的硬盘大小。

图 8:硬盘大小

选择硬盘大小后,点击“创建”完成虚拟机的创建。

图 9:虚拟机已创建

您可以点击绿色的“启动”按钮来启动您的虚拟机。然后,您需要选择一个启动盘,如下截图所示。

图 10:选择启动盘

选择您已下载的 Ubuntu ISO 镜像,然后点击“启动”以启动 Ubuntu 安装程序,如下截图所示。

图 11:Ubuntu 安装程序

现在您可以选择安装 Ubuntu。接下来,您将需要选择语言和键盘布局。之后,您应该继续接受默认设置。

最终,您将来到创建新用户的步骤,如下截图所示。

图 12:创建新用户

我选择了用户名elliot,因为我是《黑客军团》这部电视剧的忠实粉丝,而且 Elliot 在轻松地黑入 E Corp 时一直在使用 Linux!我强烈建议您选择elliot作为您的用户名,这样您就可以更轻松地跟着本书学习。

然后您可以点击“继续”,系统安装将开始,如下截图所示。

图 13:系统安装

安装过程将需要几分钟。在安装完成时,请耐心等待或自己泡杯咖啡。

安装完成后,您需要重新启动虚拟机,如下截图所示。

图 14:安装完成

您可以点击“立即重启”。之后,可能会要求您移除安装介质,您可以通过选择“设备” -+ “光驱” -+ “从虚拟驱动器中移除磁盘”来完成。

最后,您应该看到登录界面,如下截图所示。

图 15:Ubuntu 登录

现在您可以输入密码,万岁!您现在已经进入了 Linux 系统。

还有其他方法可以用来尝试 Linux 系统。例如,您可以在AWS亚马逊网络服务)上创建一个账户,并在 Amazon EC2 实例上启动一个 Linux 虚拟机。同样,您也可以在 Microsoft Azure 上创建一个 Linux 虚拟机。所以,可以说你很幸运生活在这个时代!过去,要在 Linux 上运行起来是一个痛苦的过程。

终端与 Shell

图形用户界面GUI)相当容易理解。您可以轻松地四处走动,连接到互联网并打开您的网络浏览器。所有这些都很容易,正如您在下面的截图中所看到的。

图 16:图形用户界面

您可以使用Ubuntu 软件在系统上安装新的软件程序。

您可以使用Dash与在 Microsoft Windows 上使用“开始”菜单启动应用程序的方式相同。

LibreOffice Writer是一个出色的文字处理器,与 Microsoft Word 具有相同的功能,只有一个区别;它是免费的!

现在,您可以成为一个休闲的 Linux 用户,这意味着您可以使用 Linux 来执行日常用户所做的基本任务:浏览 YouTube,发送电子邮件,搜索 Google 等。但是,要成为一个高级用户,您需要熟练使用 Linux 的命令行界面

要访问 Linux 的命令行界面,您需要打开终端仿真器,通常简称为终端

什么是终端仿真器?

终端仿真器是一种模拟物理终端(控制台)的程序。终端与 Shell(命令行界面)进行交互。

好了,现在您可能会摸着头皮问自己:“什么是 Shell?”

什么是 Shell?

Shell 是一个命令行解释器,也就是说,它是一个处理和执行命令的程序。

好了,够了,不要再讲理论了。让我们通过一个示例来理解并把所有东西联系在一起。继续打开终端,点击 Dash,然后搜索终端。您也可以使用快捷键Ctrl+Alt+T来打开终端。当终端打开时,您将看到一个新窗口,就像下面的截图所示。

图 17:终端

它看起来有点像 Microsoft Windows 上的命令提示符。好了,现在在您的终端上输入date,然后按Enter

elliot©ubuntu-linux:-$ date 
Tue Feb 17 16:39:13 CST 2020

现在让我们讨论发生了什么,date是一个打印当前日期和时间的 Linux 命令,当您按下Enter后,Shell(在幕后工作)然后执行了date命令,并在您的终端上显示了输出。

您不应该混淆终端Shell。终端是您在屏幕上看到的窗口,您可以在其中输入命令,而 Shell 负责执行命令。就是这样,没有更多,也没有更少。

您还应该知道,如果您输入任何无意义的内容,您将会收到命令未找到的错误,就像下面的例子中所示的那样:

elliot©ubuntu-linux:-$ blabla 
blabla: command not found

一些简单的命令

恭喜您学会了您的第一个 Linux 命令(date)。现在让我们继续学习更多!

通常在显示日期后会显示日历,对吧?要显示当前月份的日历,您可以运行cal命令:

图 18:cal 命令

您还可以显示整年的日历,例如,要获取完整的 2022 年日历,您可以运行:

图 19:2022 年的 cal 命令

您还可以指定一个月份,例如,要显示 1993 年 2 月的日历,您可以运行以下命令:

图 20:1993 年 2 月的 cal 命令

您现在在终端上有很多输出。您可以运行clear命令来清除终端屏幕:

图 21:清除前

这是在运行clear命令后你的终端会看起来的样子:

图 22:清除后

您可以使用lscpu命令,它是列出 CPU的缩写,来显示 CPU 架构信息:

elliot©ubuntu-linux:-$ lscpu 
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit 
Byte Order:            Little Endian
CPU(s):                1
On-line CPU(s) list:   0
Thread(s) per core:    1 
Core(s) per socket:    1 
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 61
Model name:            Intel(R) Core(TM) i5-5300U CPU© 2.30GHz Stepping: 4
CPU MHz:               2294.678
BogoMIPS:              4589.35
Hypervisor vendor:     KVM 
Virtualization type:   full 
Lid cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              3072K 
NUMA nodeO CPU(s):     0
Flags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr

您可以使用uptime命令来检查系统已运行多长时间。uptime命令还会显示:

  • 当前时间。

  • 当前已登录的用户数。

  • 过去 1、5 和 15 分钟的系统负载平均值。

elliot©ubuntu-linux:-$ uptime
18:48:04 up 4 days, 4:02, 1 user, load average: 0.98, 2.12, 3.43

您可能会对uptime命令的输出感到害怕,但不用担心,下表会为您解释输出内容。

18:48:04 输出中看到的第一件事是当前时间。
已运行 4 天 4 小时 2 分钟 这基本上是说系统已经运行了 4 天 4 小时 2 分钟。
1 个用户 目前只有一个用户登录。
负载平均值:0.98, 2.12, 3.43 过去 1、5 和 15 分钟的系统负载平均值。

表 1:uptime 命令输出

您可能以前没有听说过负载平均值。要理解负载平均值,首先必须了解系统负载。

什么是系统负载?

简单来说,系统负载是 CPU 在给定时间内执行的工作量。

因此,计算机上运行的进程(或程序)越多,系统负载就越高,运行的进程越少,系统负载就越低。现在,既然您了解了系统负载是什么,那么理解负载平均值就很容易了。

什么是负载平均值?

负载平均值是在 1、5 和 15 分钟内计算的平均系统负载。

因此,您在uptime命令输出的末尾看到的三个数字分别是 1、5 和 15 分钟的负载平均值。例如,如果您的负载平均值为:

load average: 2.00, 4.00, 6.00

然后这三个数字分别代表以下内容:

  • 2.00 --+:过去一分钟的负载平均值。

  • 4.00 --+:过去五分钟的负载平均值。

  • 6.00 --+:过去十五分钟的负载平均值。

从负载平均值的定义中,我们可以得出以下关键点:

  1. 负载平均值为0.0表示系统处于空闲状态(什么也不做)。

  2. 如果 1 分钟负载平均值高于515分钟的平均值,则这意味着您的系统负载正在增加。

  3. 如果 1 分钟负载平均值低于515分钟的平均值,则这意味着您的系统负载正在减少。

例如,负载平均值为:

load average: 1.00, 3.00, 7.00

显示系统负载随时间减少。另一方面,负载平均值为:

load average: 5.00, 3.00, 2.00

表明系统负载随时间增加。作为实验,首先通过运行uptime命令记录负载平均值,然后打开您的网络浏览器并打开多个选项卡,然后重新运行uptime;您会看到负载平均值已经增加。之后关闭浏览器并再次运行uptime,您会看到负载平均值已经减少。

您可以运行reboot命令来重新启动系统:

elliot©ubuntu-linux:-$ reboot

您可以运行pwd命令来打印当前工作目录的名称:

elliot©ubuntu-linux:-$ pwd
/home/elliot

当前工作目录是用户在特定时间工作的目录。默认情况下,当您登录到 Linux 系统时,您的当前工作目录设置为您的主目录:

/home/your_username

什么是目录?

在 Linux 中,我们将文件夹称为目录。目录是包含其他文件的文件。

您可以运行ls命令来列出当前工作目录的内容:

elliot©ubuntu-linux:-$ ls
Desktop Documents Downloads Music Pictures Public Videos

如果您想更改密码,可以运行passwd命令:

elliot©ubuntu-linux:-$ passwd 
Changing password for elliot. 
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

您可以使用hostname命令来显示系统的主机名:

elliot©ubuntu-linux:-$ hostname 
ubuntu-linux

您可以使用free命令来显示系统上的空闲和已使用内存量:

elliot©ubuntu-linux:-$ free
 total      used    free   shared   buff/cache  available 
Mem:    4039732   1838532  574864    71900      1626336    1848444
Swap:    969960         0  969960

默认情况下,free命令以千字节为单位显示输出,但只有外星人才能理解这个输出。

通过使用-h选项运行free命令,您可以获得对我们人类有意义的输出:

elliot©ubuntu-linux:-$ free -h
 total     used     free     shared     buff/cache     available
Mem:      3.9G     1.8G     516M        67M           1.6G          1.7G
Swap:     947M       OB     947M 

这样好多了,对吧?-h--human的缩写,它以人类可读的格式显示输出。

您可能已经注意到,这是我们第一次使用选项运行命令。大多数 Linux 命令都有选项,您可以使用这些选项轻微更改它们的默认行为。

您还应该知道,命令选项要么以单破折号(-)开头,要么以双破折号(--)开头。如果您使用命令选项的缩写名称,则可以使用单破折号。另一方面,如果您使用命令选项的全名,则需要使用双破折号:

elliot©ubuntu-linux:-$ free --human
 total     used     free     shared     buff/cache     available
Mem:      3.9G     1.8G     516M        67M           1.6G          1.7G
Swap:     947M       OB     947M 

如您所见,free命令的前两次运行产生了相同的输出。唯一的区别是第一次,我们使用了缩写命令选项名称-h,因此我们使用了单破折号。而第二次,我们使用了完整的命令选项名称--human,因此我们使用了双破折号。

在使用命令选项的缩写名称与完整命令选项名称时,您可以自由选择。

您可以使用df命令来显示系统上可用的磁盘空间量:

elliot©ubuntu-linux:-$ df
Filesystem     1K-blocks     Used     Available     Use%      Mounted on
udev             1989608        0       1989608       0%            /dev
tmpfs             403976     1564        402412       1%            /run
/dev/sda1       20509264  6998972      12445436      36%           /
tmpfs            2019864    53844       1966020       3%        /dev/shm
tmpfs               5120        4          5116       1%       /run/lock
tmpfs            2019864        0       2019864       0%  /sys/fs/cgroup
/dev/loop0         91648    91648             0     100% /snap/core/6130
tmpfs             403972       28        403944       1%   /run/user/121
tmpfs             403972       48        403924       1%  /run/user/1000

同样,您可能希望使用-h选项以显示更好的格式:

elliot©ubuntu-linux:-$ df -h
Filesystem       Size      Used      Avail     Use%      Mounted on
udev             1.9G         0       1.9G       0%            /dev
tmpfs            395M      1.6M       393M       1%            /run
/dev/sda1         20G      6.7G        12G      36%            /
tmpfs            2.0G       57M       1.9G       3%        /dev/shm
tmpfs            5.0M      4.0K       5.0M       1%       /run/lock
tmpfs            2.0G         0       2.0G       0%  /sys/fs/cgroup
/dev/loop0        90M       90M          0     100% /snap/core/6130
tmpfs            395M       28K       395M       1%   /run/user/121
tmpfs            395M       48K       395M       1%  /run/user/1000

如果您无法理解输出中的所有内容,不要担心,因为我将在接下来的章节中详细解释一切。本章的整个想法是让您有所了解;我们稍后将深入研究。

echo命令是另一个非常有用的命令;它允许您在终端上打印一行文本。例如,如果您想在终端上显示Cats are better than Dogs!这一行,那么您可以运行:

elliot©ubuntu-linux:-$ echo Cats are better than Dogs! 
Cats are better than Dogs!

您可能会问自己,“这有什么用?”好吧,我向您保证,当您读完本书时,您会意识到echo命令的巨大好处。

你可以在终端上花费大量时间,输入命令。有时,您可能想要重新运行一个命令,但可能忘记了命令的名称或您使用的选项,或者您只是懒得不想再次输入。无论情况如何,history命令都不会让你失望。

让我们运行history命令,看看我们得到了什么:

elliot©ubuntu-linux:-$ history
1 date
2 blabla
3 cal
4 cal 2022
5 cal feb 1993
6 clear
7 lscpu
8 uptime
9 reboot
10 pwd
11 ls
12 passwd
13 hostname
14 free
15 free -h
16 free --human
17 df
18 df -h
19 echo Cats are better than Dogs!
20 history

正如预期的那样,history命令按时间顺序显示了我们迄今为止运行的所有命令。在我的历史列表中,lscpu命令是第7个,所以如果我想重新运行lspcu,我只需要运行!7

elliot©ubuntu-linux:-$ !7 
lscpu
Architecture:         x86_64
CPU op-mode(s):       32-bit, 64-bit
Byte Order:           Little Endian 
CPU(s):               1
On-line CPU(s) list:  0
Thread(s) per core:   1
Core(s) per socket:   1
Socket(s):            1
NUMA node(s):         1
Vendor ID:            GenuineIntel
CPU family:           6
Model:                61
Model name:           Intel(R) Core(TM) i5-5300U CPU @ 2.30GHz
Stepping:             4
CPU MHz:              2294.678
BogoMIPS:             4589.35
Hypervisor vendor:    KVM
Virtualization type:  full
Lid cache:            32K
L1i cache:            32K
12 cache:             256K
13 cache:             3072K 
NUMA node0 CPU(s):    0
Flags:                fpu vme de pse tsc msr pae mce cx8 apic sep mtrr

向上和向下箭头键

您可以在命令行历史记录上上下滚动。每次按向上箭头键,您就可以在命令历史记录中向上滚动一行。

您还可以使用向下箭头键来反向滚动。

您可以使用uname命令来显示系统的内核信息。当您运行uname命令而没有任何选项时,它只会打印内核名称:

elliot©ubuntu-linux:-$ uname 
Linux

您可以使用-v选项打印当前内核版本信息:

elliot©ubuntu-linux:-$ uname -v
#33-Ubuntu SMP Wed Apr 29 14:32:27 UTC 2020

您还可以使用-r选项打印当前内核发布信息:

elliot©ubuntu-linux:-$ uname -r 
5.4.0-29-generic

您还可以使用-a选项一次打印当前内核的所有信息:

elliot©ubuntu-linux:-$ uname -a
Linux ubuntu-linux 5.4.0-29-generic #33-Ubuntu SMP
Wed Apr 29 14:32:27 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

您还可以运行lsb_release -a命令来显示您当前运行的 Ubuntu 版本:

elliot©ubuntu-linux:-$ lsb_release -a 
No LSB modules are available.
Distributor ID: Ubuntu 
Description: Ubuntu 20.04 LTS 
Release: 20.04
Codename: focal

最后,您将在本章学习的最后一个命令是exit命令,它终止当前的终端会话:

elliot©ubuntu-linux:-$ exit

一个酷炫的事实

您现在可能已经观察到,Linux 命令名称基本上与它们的功能相似。例如,pwd命令字面上代表打印工作目录ls代表列表lscpu代表列出 CPU,等等。这个事实使得记住 Linux 命令变得更容易。

恭喜!您已经完成了第一章。现在是您的第一个知识检查练习的时间。

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 显示 2023 年的整个日历。

  2. 以人类可读的格式显示系统的内存信息。

  3. 显示您的主目录的内容。

  4. 更改当前用户密码。

  5. 在您的终端上打印出“Mr. Robot 是一部很棒的电视节目!”

正确还是错误

  1. DATE命令显示当前日期和时间。

  2. 重新启动您的 Linux 系统,只需运行restart命令。

  3. 运行free -hfree --human命令之间没有区别。

  4. 如果您的平均负载值递增,系统负载随时间增加。

load average: 2.12, 3.09, 4.03
  1. 如果您的平均负载值是递减的,系统负载随时间减少。
load average: 0.30, 1.09, 2.03

攀爬树

在这一章中,你将攀爬一个非常特殊的树,那就是 Linux 文件系统。在这次攀爬的旅程中,你将学到:

  • Linux 文件系统层次结构。

  • 根目录是什么?

  • 绝对路径与相对路径。

  • 如何浏览 Linux 文件系统。

第三章:Linux 文件系统

好了,你已经在树的根部准备好攀爬了。在 Linux 中,就像实际的树一样,文件系统的开始是从根目录开始的。你可以使用cd命令后跟一个斜杠来到达根目录:

elliot@ubuntu-linux:~$ cd /

cd命令是Change Directory的缩写,是 Linux 中最常用的命令之一。没有它,你无法在 Linux 中移动。就像你的四肢(手臂和腿),你能在没有四肢的情况下爬树吗?

斜杠字符代表根目录。现在为了确保你在根目录,你可以运行pwd

elliot@ubuntu-linux:~$ pwd
/

果然,我们在 Linux 文件系统的根目录。每当你迷失方向不知道自己在哪里时,pwd就在这里拯救你。

好了,当我们还在根目录时,让我们看看里面有什么!运行ls命令来查看当前目录的内容:

elliot@ubuntu-linux:/$ ls
bin etc lib proc tmp var boot 
dev home opt root sbin usr

为了更好地查看内容,你可以使用ls命令的长列表-l选项:

elliot@ubuntu-linux:/$ ls -l
drwxr-xr-x   2 root root           4096 Dec 28 15:36 bin
drwxr-xr-x 125 root root          12288 Jan  1 11:01 etc
drwxr-xr-x  21 root root           4096 Dec 26 23:52 lib
dr-xr-xr-x 227 root root              0 Jan  3 02:33 proc
drwxrwxrwt  15 root root           4096 Jan  3 02:35 tmp
drwxr-xr-x  14 root root           4096 Jul 24 21:14 var
drwxr-xr-x   3 root root           4096 Dec 29 07:17 boot
drwxr-xr-x  18 root root           4000 Jan  3 02:33 dev
drwxr-xr-x   3 root root           4096 Dec 26 23:47 home
drwxr-xr-x   3 root root           4096 Dec 27 15:07 opt
drwx------   4 root root           4096 Dec 29 09:39 root
drwxr-xr-x   2 root root          12288 Dec 28 15:36 sbin
drwxr-xr-x  10 root root           4096 Jul 24 21:03 usr

这个输出给了你很多有价值的信息,我们将在接下来的章节中详细讨论。但现在,我们关注输出的第一列的第一个字母。看一下输出的第一列:

drwxr-xr-x 
drwxr-xr-x 
drwxr-xr-x 
drwxr-xr-x
.
.
.
.

你会看到第一个字母是d,这意味着文件是一个目录。第一个字母揭示了文件类型。输出的最后一列显示了文件名。

其他文件!

你的根目录下会有更多的文件。我只选择了最重要和最常见的文件,这些文件应该存在于每个 Linux 发行版中。所以当你看到比这本书中列出的文件更多时,不要惊慌。

现在每个目录都有特殊的用途,就像你在下表中看到的那样:

/ 这是你的文件系统的根,一切都从这里开始。
/etc 这个目录包含系统配置文件。
/home 这是所有用户(除了 root 用户)的默认主目录。
/root 这是 root 用户的主目录。
/dev 这是你的设备,比如硬盘、USB 驱动器和光驱所在的地方。
/opt 这是你可以安装额外第三方软件的地方。
/bin 这是你的系统上必要的二进制文件(程序)所在的地方。
/sbin 这是系统管理员通常使用的系统二进制文件(程序)存储的地方。
/tmp 这是临时文件存储的地方;它们通常在系统重启后被删除,所以不要在这里存储重要文件!
/var 这个目录包含可能会改变大小的文件,比如邮件储存和日志文件。
/boot 所有系统启动所需的文件都存储在这里。
/lib 这个目录包含了/bin 和/sbin 目录中必要二进制文件所需的库。库基本上是一组可以被程序使用的预编译函数。
/proc 运行进程的信息存储在这里。
/usr 这个目录包含了用户之间共享的文件和实用程序。

表 2:解释 Linux 目录

你也可以运行man hier命令来阅读更多关于 Linux 文件系统层次结构的信息:

elliot@ubuntu-linux:/$ man hier

好了,现在让我们在 Linux 目录树上进一步攀爬。看一下图 1,你就会明白为什么我们选择了一棵树来描述 Linux 文件系统的结构。

图 1:Linux 目录树

前面的图只显示了很少的文件,绝不代表整个目录树,因为 Linux 文件系统实际上包含成千上万的文件。因此,您可以将前面的图像视为实际 Linux 目录树的子树。

浏览目录树

好吧,让我们再爬一点。例如,让我们进入/home目录,看看系统上有多少用户。您只需运行cd /home命令即可:

elliot@ubuntu-linux:~$ cd /home 
elliot@ubuntu-linux:/home$

注意您的命令提示符如何更改,因为它现在显示您在主目录。

图 2:您现在在/home

现在让我们运行ls来查看/home目录的内容:

elliot@ubuntu-linux:/home$ ls 
angela elliot

这是我系统上的两个用户(除了 root 用户)。/root是 root 用户的主目录。您可能只有一个用户在/home;您将在本书后面学习如何向系统添加其他用户。

谁是 root 用户?

root 用户是允许在系统上执行任何操作的超级用户。root 用户可以安装软件,添加用户,管理磁盘分区等。root 用户的主目录是/root,不要与/(文件系统的根)混淆。

如果您想要证明您当前在/home目录,可以运行pwd命令:

elliot@ubuntu-linux:/home$ pwd
/home

确实!我们在/home目录。现在让我们进入用户elliot的主目录。现在,信不信由你,有两种方法可以导航到elliot的主目录。您可以简单地运行cd elliot命令:

elliot@ubuntu-linux:/home$ cd elliot 
elliot@ubuntu-linux:~$ pwd
/home/elliot

或者您可以运行cd /home/elliot命令:

elliot@ubuntu-linux:/home$ cd /home/elliot 
elliot@ubuntu-linux:~$ pwd
/home/elliot

图 3:现在您在/home/elliot

请注意,这两个命令都将我们带到了elliot的主目录。但是,运行cd elliot比运行cd /home/elliot要容易得多,当然。

嗯,想想吧,我们最初在/home目录,这就是为什么我们能够运行cd elliot进入/home/elliot的原因。

但是,在其他情况下,我们将被迫使用完整路径(绝对路径)/home/elliot来到达我们的目的地。为了演示,让我们首先切换到/etc目录:

elliot@ubuntu-linux:~$ cd /etc 
elliot@ubuntu-linux:/etc$ pwd
/etc

图 4:现在您在/etc

图 5:您想要进入/home/elliot

图 45可以帮助您可视化。您现在在/etc,想要进入/home/elliot。为了进入elliot的主目录,我们不能再使用短路径(相对路径)运行cd elliot命令:

elliot@ubuntu-linux:/etc$ cd elliot
bash: cd: elliot: No such file or directory

如您所见,Shell 生气了并返回了一个错误bash: cd: elliot: No such file or directory。在这种情况下,我们必须使用完整路径(绝对路径)/home/elliot

elliot@ubuntu-linux:/etc$ cd /home/elliot 
elliot@ubuntu-linux:~$ pwd
/home/elliot

如果您现在还没有注意到,我们一直在使用斜杠(/)作为目录分隔符。

目录分隔符

在 Linux 中,斜杠(/)是目录分隔符,有时也称为路径分隔符。在 Windows 中,情况正好相反,因为反斜杠(\)被用作目录分隔符。但是,要小心,因为前导斜杠是我们文件系统的根。例如,在/home/elliot/Desktop中,只有第二个和第三个斜杠是目录分隔符,但第一个斜杠代表文件系统的根。

意识到绝对路径和相对路径之间的区别是至关重要的。

绝对路径与相对路径

文件的绝对路径只是该文件的完整路径,并且始终以前导斜杠开头。例如,/opt/- google/chrome是绝对路径的一个例子。

另一方面,文件的相对路径从不以根目录开头,始终相对于当前工作目录。例如,如果您当前在/var,那么log/boot.log就是有效的相对路径。

作为一个经验法则,如果你想区分相对路径和绝对路径,看一下路径是否以根目录(斜杠)开头;如果是的话,你可以得出结论这是绝对路径,否则,这是相对路径。

下面的图表显示了相对路径Desktop/hello.txt,只有当你的当前工作目录是/home/elliot时才有效。

图 6:这是一个相对路径

下面的图片显示了绝对路径/home/elliot/Desktop,无论你当前的工作目录是什么,它都会一直有效。

图 7:这是一个绝对路径

现在让我们进入 Elliot 的Desktop目录看看他在那里有什么。我们将使用绝对路径:

elliot@ubuntu-linux:/$ cd /home/elliot/Desktop 
elliot@ubuntu-linux:~/Desktop$ pwd
/home/elliot/Desktop

我们接着运行pwd来确认我们确实在想要的目录中。现在让我们运行ls来查看 Elliot 的桌面上的内容:

elliot@ubuntu-linux:~/Desktop$ ls 
hello.txt

注意hello.txt文件在 Elliot 的桌面上,所以我们实际上可以在桌面上看到它。

图 8:Elliot 的桌面

如你在上面的图片中所见,Elliot 的桌面上有一个名为hello.txt的文件。你可以使用cat命令来查看文本文件的内容:

elliot@ubuntu-linux:~/Desktop$ cat hello.txt 
Hello Friend!
Are you from fsociety?

如果你在桌面上打开hello.txt文件,你会看到相同的内容,当然,就像你在下面的截图中看到的那样。

图 9:hello.txt 的内容

父目录和当前目录

在文件系统的每个目录下都有两个特殊的目录:

  1. 当前工作目录用一个点(.)表示

  2. 父目录用两个点(..)表示

图 10:可视化父目录和当前目录

通过几个例子很容易理解这两个目录。举个例子,让我们首先切换到/home/elliot,这样它就成为我们的当前工作目录:

elliot@ubuntu-linux:~/Desktop$ cd /home/elliot 
elliot@ubuntu-linux:~$ pwd
/home/elliot

现在运行cd .命令:

elliot@ubuntu-linux:~$ cd . 
elliot@ubuntu-linux:~$ pwd
/home/elliot

正如你所期望的,什么都没有发生!我们仍然在/home/elliot,这是因为一个点(.)代表当前工作目录。就好像你告诉某人,“去你所在的地方!”

现在运行cd ..命令:

elliot@ubuntu-linux:~$ cd .. 
elliot@ubuntu-linux:/home$ pwd
/home

我们回到了上一个目录!换句话说,我们切换到了/home/elliot的父目录,也就是/home

让我们再运行一个cd ..

elliot@ubuntu-linux:/home$ cd .. 
elliot@ubuntu-linux:/$ pwd
/

我们确实一直在回去,现在我们在我们的目录树的根目录。好吧,让我们再次运行cd ..

elliot@ubuntu-linux:/$ cd .. 
elliot@ubuntu-linux:/$ pwd
/

嗯,我们还在同一个目录!我们的路径没有改变,这是因为我们已经在我们的目录树的根目录了,所以我们无法再回去了。因此,根目录(/)是唯一一个父目录=当前目录的目录,你可以通过查看图 10来进行可视化。

你也可以插入目录分隔符cd ../..一次性回到两个目录:

elliot@ubuntu-linux:~$ pwd
/home/elliot
elliot@ubuntu-linux:~$ cd ../.. 
elliot@ubuntu-linux:/$ pwd
/

你也可以运行cd ../../..来回到三个目录,依此类推。

快速移动

现在我将向你展示一些很酷的技巧,这些技巧将使你在浏览 Linux 目录树时更快更高效。

回到家!

让我们切换到/var/log目录:

elliot@ubuntu-linux:~$ cd /var/log 
elliot@ubuntu-linux:/var/log$ pwd
/var/log

你现在可以运行cd ~命令来进入你的家目录:

elliot@ubuntu-linux:/var/log$ cd ~ 
elliot@ubuntu-linux:~$ pwd
/home/elliot

哇!让我们再做一次,但这次,我们切换到用户angela。如果你不知道,这个字符叫做波浪号,应该位于键盘上数字1键的旁边:

elliot@ubuntu-linux:~$ whoami 
elliot
elliot@ubuntu-linux:~$ su angela 
Password:
angela@ubuntu-linux:/home/elliot$ whoami 
angela

注意这里我使用了两个新命令。whoami命令打印当前登录用户的名称。我还使用了切换用户su命令来切换到用户angela。你可以使用su命令来切换到系统上的任何用户;你只需要运行su,然后跟上用户名。

现在,作为用户angela,我将导航到/var/log目录:

angela@ubuntu-linux:/home/elliot$ cd /var/log 
angela@ubuntu-linux:/var/log$ pwd
/var/log

然后我运行cd ~命令:

angela@ubuntu-linux:/var/log$ cd ~ 
angela@ubuntu-linux:~$ pwd
/home/angela

哇!我在 Angela 的主目录。无论您当前的工作目录是什么,运行cd ~命令都会直接将您带回到您的主目录。

带我回去!

现在,如果angela想尽快返回到她以前的工作目录怎么办?

运行cd -命令是将angela快速返回到她以前的工作目录的最快方法:

angela@ubuntu-linux:~$ pwd
/home/angela
angela@ubuntu-linux:~$ cd -
/var/log

酷!angela回到了/var/log。所以每当您想返回到以前的工作目录时,只需运行cd -命令。

隐藏文件

在 Linux 文件系统的每个目录下都存在当前目录 .和父目录 ..。但是当我们运行ls命令时为什么看不到它们呢?

elliot@ubuntu-linux:~/Desktop$ pwd
/home/elliot/Desktop 
elliot@ubuntu-linux:~/Desktop$ ls 
hello.txt
elliot@ubuntu-linux:~/Desktop$ ls -l 
total 4
-rw-r--r-- 1 elliot elliot 37 Jan 19 14:20 hello.txt

如您所见,我甚至尝试运行ls -l,仍然看不到当前目录或父目录。

您需要使用ls命令的-a选项如下:

elliot@ubuntu-linux:~/Desktop$ ls -a
. .. hello.txt

万岁!现在您可以看到所有文件了。-a选项显示所有文件,包括隐藏文件,当然您也可以使用完整的选项名称--all,它将做同样的事情:

elliot@ubuntu-linux:~/Desktop$ ls --all
. .. hello.txt

原来,任何以 .(点)开头的文件名都是隐藏的。

隐藏的文件名以 .开头

任何以点开头的文件名都是隐藏的。这就是为什么当前目录和父目录是隐藏的。

为了进一步演示,进入您的用户主目录并运行ls命令:

angela@ubuntu-linux:~$ ls 
Music

现在运行ls -a命令:

angela@ubuntu-linux:~$ ls -a
. .. .bash_logout .bashrc Music .profile

您现在可以看到主目录中的隐藏文件!请注意,所有隐藏的文件名都以点开头。

传递命令参数

到目前为止,我们只在当前工作目录上运行了ls命令。但是,您可以列出任何目录的内容,而无需更改到该目录。例如,如果您当前的工作目录是/home/elliot

elliot@ubuntu-linux:~$ pwd
/home/elliot

您可以通过运行ls -a /home/angela命令列出/home/angela中的所有文件:

elliot@ubuntu-linux:~$ ls -a /home/angela
. .. .bash_history .bash_logout .bashrc Music .profile 
elliot@ubuntu-linux:~$ pwd
/home/elliot 
elliot@ubuntu

我能够在/home/elliot的同时列出/home/angela的内容。这是可能的,因为ls命令接受任何文件作为参数。

什么是参数?

参数,也称为命令行参数,只是作为输入提供给命令的任何文件名或数据。

图 11:Linux 命令结构

您可以在前面的图像中看到 Linux 命令的一般结构。

在 Linux 术语中,当谈论命令选项和参数时,我们使用动词传递。为了使用正确的 Linux 术语,例如,在前面的图像中,我们说:“我们将/home/angela目录作为ls命令的参数传递。”

您会经常发现 Linux 用户非常热衷于使用正确的术语。此外,使用正确的术语可以帮助您通过工作面试并获得梦想的工作!

请注意在前面的图中,我们使用了复数名词选项参数。这是因为一些命令可以接受多个选项和参数。

例如,我们可以通过运行ls -a -l /home/angela命令来列出/home/angela中的所有文件的长列表:

elliot@ubuntu-linux:~$ ls -a -l /home/angela 
total 28
drwxr-xr-x 3 angela angela 4096 Jan 20 13:43 .
drwxr-xr-x 9  root    root 4096 Jan 17 04:37 ..
-rw------- 1 angela angela   90 Jan 20 13:43 .bash_history
-rw-r--r-- 1 angela angela  220 Apr  4  2018 .bash_logout
-rw-r--r-- 1 angela angela 3771 Apr  4  2018 .bashrc
drwxrwxr-x 2 angela angela 4096 Jan 19 19:42 Music
-rw-r--r-- 1 angela angela  807 Apr  4  2018 .profile

所以现在您可以看到/home/angela中所有文件的长列表,包括隐藏文件,还要注意这里选项的顺序无关紧要,所以如果您运行ls -l -a /home/angela命令:

elliot@ubuntu-linux:~$ ls -l -a /home/angela 
total 28
drwxr-xr-x 3 angela angela 4096 Jan 20 13:43 .
drwxr-xr-x 9   root   root 4096 Jan 17 04:37 ..
-rw------- 1 angela angela   90 Jan 20 13:43 .bash_history
-rw-r--r-- 1 angela angela  220 Apr  4  2018 .bash_logout
-rw-r--r-- 1 angela angela 3771 Apr  4  2018 .bashrc
drwxrwxr-x 2 angela angela 4096 Jan 19 19:42 Music
-rw-r--r-- 1 angela angela  807 Apr  4  2018 .profile

您将得到相同的结果。这是传递两个命令选项的示例,那么传递两个参数呢?好吧,您可以通过将/home/elliot作为第二个参数,同时对/home/angela/home/elliot中的所有文件进行长列表,而无需更改到它:

elliot@ubuntu-linux:~$ ls -l -a /home/angela /home/elliot
/home/angela:

total 28
drwxr-xr-x 3 angela angela 4096 Jan 20 13:43 .
drwxr-xr-x 9 root   root   4096 Jan 17 04:37 ..
-rw------- 1 angela angela   90 Jan 20 13:43 .bash_history
-rw-r--r-- 1 angela angela  220 Apr  4  2018 .bash_logout
-rw-r--r-- 1 angela angela 3771 Apr  4  2018 .bashrc
drwxrwxr-x 2 angela angela 4096 Jan 19 19:42  Music
-rw-r--r-- 1 angela angela  807 Apr  4  2018 .profile

/home/elliot:
total 28
drwxr-xr-x 3 elliot elliot 4096 Jan 20 16:26 .
drwxr-xr-x 9 root   root   4096 Jan 17 04:37 ..
-rw------- 1 elliot elliot   90 Jan 20 13:43 .bash_history
-rw-r--r-- 1 elliot elliot  220 Dec 26 23:47 .bash_logout
-rw-r--r-- 1 elliot elliot 3771 Dec 26 23:47 .bashrc
drwxr-xr-x 2 elliot elliot 4096 Jan 19 14:20  Desktop
-rw-r--r-- 1 elliot elliot  807 Apr 4   2018 .profile

所以现在,您可以同时看到/home/elliot/home/angela目录的内容。

touch 命令

让我们再次对/home/elliot中的所有文件进行长列表,讨论一些非常重要的事情:

elliot@ubuntu-linux:~$ ls -a -l /home/elliot 
total 28
drwxr-xr-x 3 elliot elliot 4096 Jan 20 16:26 .
drwxr-xr-x 9 root   root   4096 Jan 17 04:37 ..
-rw------- 1 elliot elliot   90 Jan 20 13:43 .bash_history
-rw-r--r-- 1 elliot elliot  220 Dec 26 23:47 .bash_logout
-rw-r--r-- 1 elliot elliot 3771 Dec 26 23:47 .bashrc
drwxr-xr-x 2 elliot elliot 4096 Jan 19 14:20  Desktop
-rw-r--r-- 1 elliot elliot  807 Apr  4  2018 .profile

关注输出的最后两列:

Jan 20 16:26 .
Jan 17 04:37 ..
Jan 20 13:43 .bash_history
Dec 26 23:47 .bash_logout
Dec 26 23:47 .bashrc
Jan 19 14:20 Desktop
Apr 4 2018 .profile

Table 3ls -a -l /home/elliot 的最后两列

你已经知道输出的最后一列(Table 3 的第二列)显示文件名,但是前一列(Table 3 的第一列)显示的所有这些日期是什么呢?

Table 3 的第一列中的日期表示每个文件的最后修改时间,即文件被修改(编辑)的最后时间。

你可以使用 touch 命令更改文件的修改时间。

为了演示,让我们首先获取 elliotDesktop 目录的修改时间,你可以通过运行 ls -l -d /home/elliot/Desktop 命令来实现:

elliot@ubuntu-linux:~$ ls -l -d /home/elliot/Desktop
drwxr-xr-x 2 elliot elliot 4096 Jan 19 14:20 /home/elliot/Desktop

请注意我们使用了 -d 选项,因此它对目录 /home/elliot/Desktop 进行了长列表,而不是列出目录的内容。

最后修改时间显示为:Jan 19 14:20

现在如果你运行 touch /home/elliot/Desktop 命令:

elliot@ubuntu-linux:~$ touch /home/elliot/Desktop 
elliot@ubuntu-linux:~$ ls -l -d /home/elliot/Desktop
drwxr-xr-x 2 elliot elliot 4096 Jan 20 19:42 /home/elliot/Desktop 
elliot@ubuntu-linux:~$ date
Sun Jan 20 19:42:08 CST 2020

你会看到目录 /home/elliot/Desktop 的最后修改时间现在已经更改为 Jan 20 19:42,这反映了当前时间。

当然,你会在你的系统上得到不同的结果,因为你不会和我同时运行命令。

好的,很好,现在我们明白了 touch 命令可以用来更新文件的修改时间。它还能做其他事情吗?嗯,让我们看看。

如果我们尝试更新一个不存在的文件的修改时间会发生什么?只有尝试才能知道。请注意,用户 elliot 的主目录中只有一个可见(非隐藏)文件,那就是 Desktop 目录:

elliot@ubuntu-linux:~$ pwd
/home/elliot
elliot@ubuntu-linux:~$ ls -l 
total 4
drwxr-xr-x 2 elliot elliot 4096 Jan 20 19:42 Desktop

看看当用户 elliot 运行 touch blabla 命令时会发生什么:

elliot@ubuntu-linux:~$ touch blabla 
elliot@ubuntu-linux:~$ ls -l
total 4
-rw-r--r-- 1 elliot elliot    0 Jan 20 20:00 blabla
drwxr-xr-x 2 elliot elliot 4096 Jan 20 19:42 Desktop

它创建了一个名为 blabla 的空文件。

你可以使用 touch 命令做两件事:

  1. 你可以更新现有文件的最后修改和访问时间。

  2. 你可以创建新的空文件。

touch 命令只能创建常规文件;它不能创建目录。另外,请注意它更新修改和访问时间,那么有什么区别呢?

  • 修改时间 > 文件最后一次被更改或修改的时间。

  • 访问时间 > 文件最后一次被访问(读取)的时间。

默认情况下,touch 命令会同时更改文件的修改和访问时间。我在 elliot 的主目录中创建了三个文件:file1file2file3

elliot@ubuntu-linux:~$ ls -l 
total 8
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
-rw-r--r-- 1 elliot elliot    0 Feb 29  2004 file1
-rw-r--r-- 1 elliot elliot    0 Apr 11  2010 file2
-rw-r--r-- 1 elliot elliot    0 Oct  3  1998 file3

现在只更改 file1 的修改时间。我们向 touch 命令传递 -m 选项:

elliot@ubuntu-linux:~$ touch -m file1 
elliot@ubuntu-linux:~$ ls -l
total 8
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:08 file1
-rw-r--r-- 1 elliot elliot    0 Apr 11  2010 file2
-rw-r--r-- 1 elliot elliot    0 Oct  3  1998 file3 
elliot@ubuntu-linux:~$

正如你所看到的,file1 的修改时间现在已经改变。我答应过只更改修改时间,对吧?如果你向 ls 命令传递 -u 选项和 -l 选项,你将得到最后访问时间而不是修改时间:

elliot@ubuntu-linux:~$ ls -l 
total 8
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
-rw-r--r-- 1 elliot elliot 0    Jan 25 23:08 file1
-rw-r--r-- 1 elliot elliot 0    Apr 11  2010 file2
-rw-r--r-- 1 elliot elliot 0    Oct 3   1998 file3
elliot@ubuntu-linux:~$ ls -l -u 
total 8 
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
-rw-r--r-- 1 elliot elliot 0    Feb 29 2004  file1
-rw-r--r-- 1 elliot elliot 0    Apr 11 2010  file2
-rw-r--r-- 1 elliot elliot 0    Oct 3  1998  file3

正如你所看到的,file1 的最后修改时间已经改变为 Jan 25 23:08,但访问时间保持不变:Feb 29 2004。这一次,让我们只改变 file2 的访问时间。为此,我们向 touch 命令传递 -a 选项:

elliot@ubuntu-linux:~$ touch -a file2 
elliot@ubuntu-linux:~$ ls -l
total 8
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:08 file1
-rw-r--r-- 1 elliot elliot    0 Apr 11  2010 file2
-rw-r--r-- 1 elliot elliot    0 Oct  3  1998 file3 
elliot@ubuntu-linux:~$ ls -l -u
total 8
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
-rw-r--r-- 1 elliot elliot   0  Feb 29  2004 file1
-rw-r--r-- 1 elliot elliot   0  Jan 25 23:20 file2
-rw-r--r-- 1 elliot elliot   0  Oct  3  1998 file3 
elliot@ubuntu-linux:~$

正如你所看到的,file2 的修改时间保持不变,但访问时间已更改为当前时间。现在要同时更改 file3 的修改和访问时间,你可以运行不带选项的 touch 命令:

elliot@ubuntu-linux:~$ ls -l file3
-rw-r--r-- 1 elliot elliot 0 Oct 3 1998 file3 
elliot@ubuntu-linux:~$ touch file3 
elliot@ubuntu-linux:~$ ls -l file3
-rw-r--r-- 1 elliot elliot 0 Jan 25 23:27 file3 
elliot@ubuntu-linux:~$ ls -l -u file3
-rw-r--r-- 1 elliot elliot 0 Jan 25 23:27 file3

太棒了!你还可以向 ls 命令传递 -t 选项,按修改时间排序列出文件,最新的排在前面:

elliot@ubuntu-linux:~$ ls -l -t 
total 8
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:27 file3
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:08 file1
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
-rw-r--r-- 1 elliot elliot    0 Apr 11  2010 file2

你可以添加 -u 选项以按访问时间排序:

elliot@ubuntu-linux:~$ ls -l -t -u 
total 8
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:27 file3
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:20 file2
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:20 file1
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop

你还可以传递 -r 选项来反向排序:

elliot@ubuntu-linux:~$ ls -l -t -r 
total 8
-rw-r--r-- 1 elliot elliot    0 Apr 11  2010 file2
drwxr-xr-x 6 elliot elliot 4096 Jan 25 22:13 Desktop
drwxr-xr-x 3 elliot elliot 4096 Jan 25 22:18 dir1
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:08 file1
-rw-r--r-- 1 elliot elliot    0 Jan 25 23:27 file3

创建目录

在 Linux 中创建目录,我们使用 mkdir 命令,它是 make directory 的缩写。

elliot 的桌面上,通过运行 mkdir games 命令创建一个名为 games 的目录:

elliot@ubuntu-linux:~/Desktop$ mkdir games 
elliot@ubuntu-linux:~/Desktop$ ls -l 
total 8
drwxr-xr-x 2 elliot elliot 4096 Jan 20 20:20 games
-rw-r--r-- 1 elliot elliot 37 Jan 19 14:20 hello.txt 
elliot@ubuntu-linux:~/Desktop$

请注意,我的当前工作目录是 /home/elliot/Destkop;这就是为什么我能够使用相对路径的原因。

图 12:桌面上创建的 games 目录

您还可以同时创建多个目录。例如,您可以通过运行mkdir Music Movies Books命令在桌面上创建三个目录-MusicMoviesBooks

elliot@ubuntu-linux:~/Desktop$ mkdir Music Movies Books 
elliot@ubuntu-linux:~/Desktop$ ls -l
total 20
drwxr-xr-x 2 elliot elliot 4096 Jan 21 01:54 Books
drwxr-xr-x 2 elliot elliot 4096 Jan 20 20:20 games
-rw-r--r-- 1 elliot elliot   37 Jan 19 14:20 hello.txt
drwxr-xr-x 2 elliot elliot 4096 Jan 21 01:54 Movies
drwxr-xr-x 2 elliot elliot 4096 Jan 21 01:54 Music

图 13:在桌面上创建的目录

您还可以使用-p选项创建整个目录路径。例如,您可以通过运行mkdir -p dir1/dir2/dir3命令创建路径/home/elliot/dir1/dir2/dir3

elliot@ubuntu-linux:~$ pwd
/home/elliot
elliot@ubuntu-linux:~$ mkdir -p dir1/dir2/dir3 
elliot@ubuntu-linux:~$ ls 
blabla Desktop dir1 
elliot@ubuntu-linux:~$ cd dir1 
elliot@ubuntu-linux:~/dir1$ ls 
dir2
elliot@ubuntu-linux:~/dir1$ cd dir2 
elliot@ubuntu-linux:~/dir1/dir2$ ls 
dir3
elliot@ubuntu-linux:~/dir1/dir2$ cd dir3 
elliot@ubuntu-linux:~/dir1/dir2/dir3$ pwd
/home/elliot/dir1/dir2/dir3 
elliot@ubuntu-linux:~/dir1/dir2/dir3$

它在/home/elliot目录中创建了dir1,然后在dir1中创建了dir2,最后在dir2中创建了dir3

您可以使用递归的-R选项对/home/elliot/dir1进行递归列表,并查看/home/elliot/dir1下的所有文件,而无需更改每个目录:

elliot@ubuntu-linux:~$ ls -R dir1 
dir1:
dir2

dir1/dir2:
dir3

dir1/dir2/dir3: 
elliot@ubuntu-linux:~$

正如你所看到的,它列出了/home/elliot/dir1下的所有文件。它甚至显示了层次结构。

您还可以通过将它们包含在一对大括号中,并且每个子目录之间用逗号分隔,来创建具有多个子目录的新目录,就像以下示例中一样:

elliot@ubuntu-linux:~/dir1/dir2/dir3$ mkdir -p dir4/{dir5,dir6,dir7} 
elliot@ubuntu-linux:~/dir1/dir2/dir3$ ls -R dir4
dir4:
dir5 dir6 dir7 

dir4/dir5: 

dir4/dir6:

dir4/dir7:

正如您所看到的,我们创建了dir4,并在其中创建了三个目录-dir5dir6dir7

组合命令选项

您已经学会了许多可以与ls命令一起使用的不同选项。表 4总结了到目前为止我们使用过的所有选项。

ls 选项 作用
-l 文件的长格式和详细列表。
-a 列出隐藏文件。
-d 仅列出目录本身,而不是它们的内容。
-t 按修改时间对文件进行排序。
-u -l一起使用时,显示访问时间而不是修改时间。与-lt一起使用时,将按访问时间排序并显示访问时间。
-r 将列表顺序反转。
-R 递归列出子目录。

表 4:常用 ls 命令选项

您经常会希望同时使用两个或更多的命令选项。例如,ls -a -l通常用于对目录中的所有文件进行长列表。

此外,ls -l -a -t -r是一个非常受欢迎的组合,因为有时您可能希望按修改时间排序文件的列表(从最旧到最新)。因此,组合命令选项更有效,因此运行ls -latr命令:

elliot@ubuntu-linux:~$ ls -latr 
total 120
-rw-r--r--  1 elliot elliot       0    Apr 11  2010 file2
-rw-r--r--  1 elliot elliot     807    Dec 26 23:47 .profile
-rw-r--r--  1 elliot elliot    3771    Dec 26 23:47 .bashrc
drwxr-xr-x  9 root   root      4096    Jan 17 04:37 ..
-rw-r--r--  1 elliot elliot     220    Jan 20 17:23 .bash_logout
drwxr-xr-x  6 elliot elliot    4096    Jan 25 22:13 Desktop
-rw-r--r--  1 elliot elliot       0    Jan 25 23:08 file1
-rw-r--r--  1 elliot elliot       0    Jan 25 23:27 file3
drwxr-xr-x  3 elliot elliot    4096    Jan 25 23:52 dir1
-rw-------  1 elliot elliot    3152    Jan 26 00:01 .bash_history
drwxr-xr-x 17 elliot elliot    4096    Jan 30 23:32 .

将产生与运行ls -l -a -t -r命令相同的结果:

elliot@ubuntu-linux:~$ ls -l -a -t -r 
total 120
-rw-r--r--  1 elliot elliot    0 Apr 11  2010 file2
-rw-r--r--  1 elliot elliot  807 Dec 26 23:47 .profile
-rw-r--r--  1 elliot elliot 3771 Dec 26 23:47 .bashrc
drwxr-xr-x  9 root   root   4096 Jan 17 04:37 ..
-rw-r--r--  1 elliot elliot  220 Jan 20 17:23 .bash_logout
drwxr-xr-x  6 elliot elliot 4096 Jan 25 22:13 Desktop
-rw-r--r--  1 elliot elliot    0 Jan 25 23:08 file1
-rw-r--r--  1 elliot elliot    0 Jan 25 23:27 file3
drwxr-xr-x  3 elliot elliot 4096 Jan 25 23:52 dir1
-rw-------  1 elliot elliot 3152 Jan 26 00:01 .bash_history
drwxr-xr-x 17 elliot elliot 4096 Jan 30 23:32 .

在本章结束之前,我想向您展示一个非常酷的技巧。首先,让我们创建一个名为averylongdirectoryname的目录:

elliot@ubuntu-linux:~$ mkdir averylongdirectoryname 
elliot@ubuntu-linux:~$ ls -ld averylongdirectoryname
drwxr-xr-x 2 elliot elliot 4096 Mar 2 12:57 averylongdirectoryname

制表完成是 Linux 命令行中最有用的功能之一。您可以使用此功能让 shell 自动完成(建议)命令名称和文件路径。为了演示,输入(不要运行)以下文本到您的终端:

elliot@ubuntu-linux:~$ cd ave

现在按下键盘上的Tab键,shell 将自动为您完成目录名称:

elliot@ubuntu-linux:~$ cd averylongdirectoryname/

相当酷!好的,这就是本章的结束,现在是时候进行可爱的知识检查了。

知识检查

对于以下练习,打开终端并尝试解决以下任务:

  1. /var/log中的所有文件进行长列表。

  2. 显示文件/etc/hostname的内容。

  3. /home/elliot中创建三个文件-file1file2file3

  4. 列出elliot的主目录中的所有文件(包括隐藏文件)。

  5. /home/elliot中创建一个名为fsociety的目录。

真或假

  1. /home/root是 root 用户的主目录。

  2. dir1/dir2/dir3是绝对路径的一个例子。

  3. /home/elliot/Desktop是绝对路径的一个例子。

  4. touch -m file1将更新file1的访问时间。

  5. mkdir dir1 dir2 dir3将创建三个目录-dir1dir2dir3

见编辑器

首先,让我告诉您一些可能会让您感到惊讶的事情。Linux 实现了所谓的“一切皆文件”的哲学。这意味着在您的 Linux 系统上,一切都由文件表示。例如,您的硬盘由一个文件表示。运行的程序(进程)由一个文件表示。甚至您的外围设备,如键盘、鼠标和打印机,都由文件表示。

说到这一点,“一切皆文件”的哲学的一个直接结果是,Linux 管理员花费大量时间编辑和查看文件。因此,您经常会看到 Linux 管理员非常擅长使用文本编辑器。本章就是专门讲这个的。我希望您能够非常熟练地使用 Linux 中的各种文本编辑器。

有很多,我是说很多文本编辑器可以使用。但是,在本章中,我将介绍最受欢迎的 Linux 编辑器,这些编辑器可以完成工作。

第四章:图形编辑器 - gedit 和 kate

我们首先从那些最基本和简单的编辑器开始。这些是图形编辑器!如果您使用任何 Linux 发行版的GNOME版本,那么默认情况下会安装文本编辑器gedit。另一方面,如果您使用 Linux 的KDE版本,那么默认情况下会安装文本编辑器kate

桌面环境

GNOME 和 KDE 是桌面环境的两个例子。每个桌面环境都实现了不同的图形用户界面,这是说您的桌面看起来会有所不同的一种花哨的方式!

无论如何,在图形编辑器上真的没有太多可以讨论的。它们非常直观和易于使用。例如,如果您想要使用gedit查看文本文件,那么您可以运行gedit命令,后面跟上任何文件名:

elliot@ubuntu-linux:~$ gedit /proc/cpuinfo

这将打开gedit图形编辑器,并显示您的 CPU 信息。

图 1:使用 gedit 打开/proc/cpuinfo

如果您没有gedit而是有kate,那么您可以运行:

elliot@ubuntu-linux:~$ kate /proc/cpuinfo

图 2:使用 kate 打开/proc/cpuinfo

您还可以使用图形编辑器在系统上创建新文件。例如,如果您想在/home/elliot中创建一个名为cats.txt的文件,那么您可以运行gedit /home/elliot/cats.txt命令:

elliot@ubuntu-linux:~$ gedit /home/elliot/cats.txt

图 3:使用 gedit 创建 cats.txt

现在插入一行“I love cats!”然后保存并关闭文件。文件cats.txt现在存在于我的主目录中,我可以使用cat命令查看它:

elliot@ubuntu-linux:~$ pwd
/home/elliot
elliot@ubuntu-linux:~$ ls -l cats.txt
-rw-r--r-- 1 elliot elliot 13 Feb 2 14:54 cats.txt 
elliot@ubuntu-linux:~$ cat cats.txt
I love cats!

同样,您可以使用任何其他图形文本编辑器在系统上创建文件。

好了!关于图形文本编辑器的讨论就到此为止。让我们继续探索非图形文本编辑器的严肃世界。

nano 编辑器

nano编辑器是一个非常流行且易于使用的命令行编辑器。您可以通过运行nano命令来打开nano编辑器:

elliot@ubuntu-linux:~$ nano

这将打开您的nano编辑器,您应该会看到以下截图中的屏幕:

图 4:在 nano 内部

现在添加以下截图中显示的六行:

图 5:添加这六行

看一下nano编辑器屏幕底部;您会看到很多快捷键:

图 6:nano 快捷键

我在下表中列出了所有有用的 nano 快捷键:

nano 快捷方式 它的作用
Ctrl+O 保存当前文件(写出)。
Ctrl+K 剪切当前行并将其存储在缓冲区中。
Ctrl+U 粘贴存储在缓冲区中的行。
Ctrl+W 在文件中搜索字符串(单词)。
Ctrl+** 用另一个字符串替换文件中的字符串(单词)。
Ctrl+R 读取另一个文件。
Ctrl+G 查看如何使用 nano 的帮助信息。
Ctrl+V 转到下一页。
Ctrl+Y 转到上一页。
Ctrl+X 退出 nano 编辑器。

表 5:nano 快捷键

请注意,按下Ctrl+O快捷键是通过按下Ctrl,然后按字母O触发的。您不必按下+键或大写字母O

现在让我们使用快捷键Ctrl+O保存文件;它会要求您输入文件名,您可以输入facts.txt

图 7:保存文件

然后按Enter确认。现在让我们退出nano编辑器(使用Ctrl+X快捷键)来验证文件facts.txt是否已创建:

elliot@ubuntu-linux:~$ ls -l facts.txt
-rw-r--r-- 1 elliot elliot 98 Apr 30 15:17 facts.txt

现在让我们再次打开facts.txt来修复我们添加的错误事实!要用nano编辑器打开文件facts.txt,您可以运行nano facts.txt命令:

elliot@ubuntu-linux:~$ nano facts.txt

文件facts.txt中的第一行说“苹果是蓝色的。”我们肯定需要纠正这个错误的事实,所以让我们使用快捷键Ctrl+**将单词blue替换为red

当您按下Ctrl+**时,它会要求您输入要替换的单词;您可以输入blue,如下面的截图所示:

图 8:要替换的单词

Enter,然后它会要求您输入替换的单词。您可以输入red,如下面的截图所示:

图 9:替换单词

然后按Enter,它将遍历单词blue的每个实例,并询问您是否要替换它。幸运的是,我们只有一个blue的出现。

图 10:用红色替换蓝色

Y,嘭!单词red替换了blue

图 11:红色替换蓝色

这里还有一个词需要改变。我们都同意地球不是平的,对吧?希望我们都同意!现在让我们像之前一样精确地用单词round替换flat,结果应该像下面的截图所示:

图 12:用圆形替换平的

现在让我们保存并退出文件。因此我们使用Ctrl+O快捷键保存,然后使用Ctrl+X退出。

nano编辑器非常简单易用。熟能生巧,所以您使用得越多,它对您来说就会变得越容易。您可以练习表 5中的所有快捷键。

vi 编辑器

nano编辑器通常是初学者的首选编辑器。它是一个很棒的编辑器,但我们只能说它不是最高效的编辑器。vi编辑器是一个更高级的 Linux 编辑器,具有大量功能,并且是高级 Linux 用户中最受欢迎的编辑器。

让我们用vi编辑器打开facts.txt文件;为此,您运行vi facts.txt命令:

elliot@ubuntu-linux:~$ vi facts.txt

这将打开vi编辑器,如下面的截图所示:

图 13:在 vi 中打开 facts.txt 文件

nano编辑器不同,vi编辑器以两种不同的模式工作:

  1. 插入模式

  2. 命令模式

插入模式使您能够在文件中插入文本。另一方面,命令模式允许您执行复制、粘贴和删除文本等操作。命令模式还允许您搜索和替换文本以及许多其他操作。

插入模式

默认情况下,您首次打开vi编辑器时会进入命令模式,而在命令模式下无法插入文本。要插入文本,您需要切换到插入模式。有几种方法可以切换到插入模式;表 6列出了所有方法。

功能
i 在当前光标位置之前插入文本。
I 在当前行的开头插入文本。
a 在当前光标位置之后添加文本。
A 在当前行的末尾添加文本。
o 在当前行下方创建一个新行。
O 在当前行上方创建一个新行。

表 6:vi 插入模式

您可以使用箭头键在vi编辑器中导航,就像在nano编辑器中一样。现在导航到文件facts.txt的最后一行,然后按字母o切换到insert模式。现在您可以添加一行“Linux is cool!”

图 14:在 vi 中添加一行

insert模式下,您可以添加任意多的文本。要切换回command模式,您需要按下Esc键。

图 15:在插入模式和命令模式之间切换

上述屏幕截图说明了如何在command模式和insert模式之间来回切换。

命令模式

除了添加文本之外,您想做的任何事情都可以从command模式中实现。您可以在vi编辑器中使用大量命令。您可能会认为我在开玩笑,但是有很多关于vi编辑器的书籍和课程。但是,“表 7”将让您熟悉vi编辑器,并列出了您可以使用的最流行的命令。

vi 命令 它的作用
yy 复制(yank)当前行。
3yy 复制(yank)三行(从当前行开始)。
yw 复制(yank)光标位置开始的一个单词。
2yw 复制(yank)光标位置开始的两个单词。
p 在当前光标位置之后粘贴。
P 在当前光标位置之前粘贴。
dd 剪切(删除)当前行。
4dd 剪切(删除)四行(从当前行开始)。
dw 剪切(删除)光标位置开始的一个单词。
x 删除光标位置的字符。
u 撤销上一次更改。
U 撤销对该行的所有更改。
/red 在文件中搜索单词red
:%s/bad/good good替换bad
设置行号 显示行号。
:set nonumber 隐藏行号。
:7 转到第 7 行。
G 跳转到文件末尾。
gg 跳转到文件开头。

表 7:vi 命令

正如您所看到的,“表 7”有很多命令,所以我不会逐一介绍所有的命令;这留给您作为练习。但是,我将讨论一些命令,以帮助您开始使用vi编辑器。

让我们首先显示行号,因为这将使我们的生活更加轻松!要做到这一点,您可以运行:set number 命令,如下面的屏幕截图所示:

图 16:显示行号

现在让我们复制第 4 行。您需要确保光标在第 4 行上;您可以通过运行:4命令来实现这一点,如下面的屏幕截图所示:

图 17:转到第 4 行

现在按下序列yy,它会复制整行。让我们在文件末尾粘贴三次。因此,导航到最后一行,然后按* p *三次,它会将复制的行粘贴三次,如下面的屏幕截图所示:

图 18:在 vi 中复制和粘贴

好了!让我们将单词cool替换为awesome,因为我们都知道 Linux 不仅仅是酷;它是令人敬畏的!要做到这一点,您可以运行:%s/cool/awesome命令,如下面的屏幕截图所示:

图 19:用 awesome 替换 cool

让我们也将单词Roses替换为Cherries,因为我们都知道并不是所有的玫瑰都是红色的。要做到这一点,运行:%s/Roses/Cherries命令,如下面的屏幕截图所示:

图 20:用 Cherries 替换 Roses

它甚至会告诉您发生了多少次替换。

酷提示

您应该知道:%s/old/new只会替换所有行中单词old的第一次出现。要替换所有行中单词old的所有出现,应使用全局选项:%s/old/new/g

要理解并理解上面的提示,向您的facts.txt文件添加行“蓝蓝蓝蓝”,并尝试使用:%s/blue/purple命令将单词blue替换为purple。您会看到它只会替换第一个blue的出现。要使其替换所有blue的出现,您必须使用全局选项

:%s/blue/purple/g

保存并退出 vi

最终,当您完成在vi中查看或编辑文件时,您会想要退出vi编辑器。您可以使用多种方法退出vi编辑器,表 8列出了所有方法。

vi 命令 它的作用
:w 保存文件但不退出vi
:wq 保存文件并退出vi
ZZ 保存文件并退出vi(与:wq相同,只是更快!)。
:x 保存文件并退出vi(与:wqZZ相同)。
:q 不保存退出vi
:q! 强制退出vi而不保存。

表 8:保存和退出 vi

所以让我们保存文件并退出vi编辑器。当然,您可以使用以下任何命令:

  1. :wq

  2. :x

  3. ZZ

它们都实现了相同的结果,即保存并退出vi

图 21:保存并退出 vi

如果您成功退出了vi编辑器,我要祝贺您,因为您是精英中的一员。互联网上有数百个关于一些人打开vi编辑器后从未能退出的模因和漫画!

文件查看命令

在某些情况下,您可能只想查看文件而不编辑它。虽然您仍然可以使用文本编辑器如nanovi来查看文件,但在 Linux 中有更快的查看文件的方法。

cat 命令

cat命令是 Linux 中最受欢迎和经常使用的命令之一。catconcatenate的缩写)命令将文件连接并打印到标准输出(终端)。

要查看我们创建的facts.txt文件,可以运行cat facts.txt命令:

elliot@ubuntu-linux:~$ cat facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.

现在,您可以在终端舒适地查看facts.txt文件的内容,而无需打开任何文本编辑器。

cat命令不仅可以查看文件,还可以连接(放在一起)文件。为了演示,使用您喜欢的文本编辑器创建以下三个文件:

  1. file1.txt(插入行“第一个文件”)

  2. file2.txt(插入行“第二个文件”)

  3. file3.txt(插入行“第三个文件”)

现在让我们使用cat命令查看这三个文件的每一个:

elliot@ubuntu-linux:~$ cat file1.txt 
First File
elliot@ubuntu-linux:~$ cat file2.txt 
Second File
elliot@ubuntu-linux:~$ cat file3.txt 
Third File

现在让我们通过运行cat file1.txt file2.txt命令来连接file1.txtfile2.txt

elliot@ubuntu-linux:~$ cat file1.txt file2.txt 
First File
Second File

我们还可以连接所有三个文件:

elliot@ubuntu-linux:~$ cat file1.txt file2.txt file3.txt 
First File
Second File 
Third File

请记住,顺序很重要;例如,运行cat file2.txt file1.txt命令:

elliot@ubuntu-linux:~$ cat file2.txt file1.txt 
Second File
First File

这将在file1.txt之前输出file2.txt的文本。

tac 命令

tac命令是cat命令的孪生兄弟。它基本上是反向编写的cat,它做的事情与cat命令相同,但是以相反的方式!

例如,如果您想以相反的顺序查看facts.txt文件,可以运行tac facts.txt命令:

elliot@ubuntu-linux:~$ tac facts.txt 
Cherries are red.
Cherries are red.
Cherries are red.
Linux is awesome!
Earth is round.
Sky is high.
Cherries are red.
Bananas are yellow.
Grapes are green.
Apples are red.

tac命令也可以像cat命令一样连接文件。

more 命令

使用cat命令查看文件是一个不错的选择,当文件很小,且没有很多行文本需要显示时。如果要查看一个大文件,最好使用more命令。more命令一次显示文件的一页内容;它基本上是一个分页程序。

让我们用more命令查看文件/etc/services的内容:

elliot@ubuntu-linux:~$ more /etc/services 
# Network services, Internet style
# Note that it is presently the policy of IANA to assign a single well-known 
# port number for both TCP and UDP; hence, officially ports have two entries 
# even if the protocol doesn't support UDP operations.

tcpmux 1/tcp # TCP port service multiplexer 
systat 11/tcp users
netstat 15/tcp ftp 21/tcp
fsp 21/udp fspd
ssh 22/tcp # SSH Remote Login Protocol 
telnet 23/tcp
smtp 25/tcp mail 
whois 43/tcp nicname
tacacs 49/tcp # Login Host Protocol (TACACS) 
tacacs 49/udp
--More--(7%)

它会显示/etc/services文件的第一页,并在底部显示一个百分比值,显示你已经浏览了文件的进度。你可以使用以下键在more中导航:

  • Enter > 向下滚动一行。

  • 空格键 > 前往下一页。

  • b > 返回上一页。

  • q > 退出。

/etc/services文件存储了许多可以在 Linux 上运行的服务(应用程序)的信息。

less 命令

less命令是more命令的改进版本。是的,你读对了;less 比 more 更好!事实上,著名的成语less is more源于lessmore提供更多的想法。

less命令是另一个分页程序,就像more一样;它允许你一次查看一个页面的文本文件。less的优点是你可以使用上/下箭头键在文件中导航。此外,lessmore更快。

你可以通过运行以下命令使用less查看/etc/services文件:

elliot@ubuntu-linux:~$ less /etc/services

你也可以在less中使用more导航键。

正面还是反面?

正如其名称所示,head命令显示文件的前几行。默认情况下,它显示文件的前十行。例如,我们知道facts.txt中有十行,因此运行head facts.txt命令将显示所有文件内容:

elliot@ubuntu-linux:~$ head facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.

你也可以传递-n选项来指定你希望查看的行数。例如,要显示facts.txt的前三行,你可以运行head -n 3 facts.txt命令:

elliot@ubuntu-linux:~$ head -n 3 facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.

另一方面,tail命令显示文件的最后几行。默认情况下,它显示最后十行。你也可以使用-n选项来指定你希望查看的行数。例如,要显示facts.txt中的最后两行,你可以运行tail -n 2 facts.txt命令:

elliot@ubuntu-linux:~$ tail -n 2 facts.txt 
Cherries are red.
Cherries are red.

你知道现在是几点吗?是时候进行一些知识检查了。

知识检查

对于以下练习,打开你的终端并尝试解决以下任务:

  1. 只查看文件facts.txt的前两行。

  2. 只查看文件facts.txt的最后一行。

  3. 以相反的顺序显示文件facts.txt的内容。

  4. 使用vi编辑器打开文件facts.txt

  5. 退出vi编辑器,认为自己是精英之一。

复制、移动和删除文件

如果您以前拥有过计算机,那么您就知道能够在文件之间复制和移动文件是多么重要。这就是为什么我专门写了一整章来讨论这个问题:复制、移动和删除文件。

第五章:复制一个文件

有时您需要复制单个文件。幸运的是,在命令行上这是一个简单的操作。我在我的主目录中有一个名为cats.txt的文件:

elliot@ubuntu-linux:~$ cat cats.txt 
I love cars!
I love cats!
I love penguins!
elliot@ubuntu-linux:~$

我可以使用cp命令复制名为cats.txt的文件并命名为copycats.txt,方法如下:

elliot@ubuntu-linux:~$ cp cats.txt copycats.txt 
elliot@ubuntu-linux:~$ cat copycats.txt
I love cars!
I love cats!
I love penguins!
elliot@ubuntu-linux:~$

如您所见,复制的文件copycats.txt与原始文件cats.txt具有相同的内容。

我也可以将文件cats.txt复制到另一个目录。例如,我可以通过运行cp cats.txt /tmp命令将文件cats.txt复制到/tmp中:

elliot@ubuntu-linux:~$ cp cats.txt /tmp
elliot@ubuntu-linux:~$ cd /tmp
elliot@ubuntu-linux:/tmp$ ls
cats.txt
elliot@ubuntu-linux:/tmp$

请注意,复制的文件与原始文件具有相同的名称。我也可以在/tmp中用不同的名称再复制一份:

elliot@ubuntu-linux:~$ cp cats.txt /tmp/cats2.txt
elliot@ubuntu-linux:~$ cd /tmp
elliot@ubuntu-linux:/tmp$ ls 
cats2.txt  cats.txt
elliot@ubuntu-linux:/tmp$

复制多个文件

您可能还想一次复制多个文件。为了演示,让我们首先在 Elliot 的主目录中创建三个文件apple.txtbanana.txtcarrot.txt

elliot@ubuntu-linux:~$ touch apple.txt banana.txt carrot.txt
elliot@ubuntu-linux:~$ ls
apple.txt carrot.txt copycats.txt dir1 
banana.txt cats.txt Desktop
elliot@ubuntu-linux:~$

要将三个新创建的文件复制到/tmp,您可以运行cp apple.txt ba- nana.txt carrot.txt /tmp命令:

elliot@ubuntu-linux:~$ cp apple.txt banana.txt carrot.txt /tmp
elliot@ubuntu-linux:~$ cd /tmp
elliot@ubuntu-linux:/tmp$ ls
apple.txt banana.txt carrot.txt cats2.txt cats.txt
elliot@ubuntu-linux:/tmp$

小菜一碟!一般来说,cp命令遵循以下语法:

cp source_file(s) destination

复制一个目录

您可能还想复制整个目录;这也很容易实现。为了演示,在您的主目录中创建一个名为cities的目录,并在cities中创建三个文件paristokyolondon,如下所示:

elliot@ubuntu-linux:~$ mkdir cities
elliot@ubuntu-linux:~$ cd cities/
elliot@ubuntu-linux:~/cities$ touch paris tokyo london
elliot@ubuntu-linux:~/cities$ ls
london paris tokyo

现在,如果您想将cities目录复制到/tmp,您必须向cp命令传递递归的-r选项,如下所示:

elliot@ubuntu-linux:~/cities$ cd ..
elliot@ubuntu-linux:~$ cp -r cities /tmp

如果您省略了-r选项,将会收到错误消息:

elliot@ubuntu-linux:~$ cp cities /tmp
cp: -r not specified; omitting directory 'cities'

您可以通过列出/tmp中的文件来验证cities目录是否已复制到/tmp中:

elliot@ubuntu-linux:~$ cd /tmp
elliot@ubuntu-linux:/tmp$ ls
apple.txt banana.txt carrot.txt cats2.txt cats.txt cities
elliot@ubuntu-linux:/tmp$ ls cities
london paris tokyo

复制多个目录

您还可以像复制多个文件一样复制多个目录;唯一的区别是您必须向cp命令传递递归的-r选项。

为了演示,在 Elliot 的主目录中创建三个目录d1d2d3

elliot@ubuntu-linux:~$ mkdir d1 d2 d3

现在,您可以通过运行cp -r d1 d2 d3 /tmp命令将所有三个目录复制到/tmp中:

elliot@ubuntu-linux:~$ cp -r d1 d2 d3 /tmp
elliot@ubuntu-linux:~$ cd /tmp
elliot@ubuntu-linux:/tmp$ ls
apple.txt banana.txt carrot.txt cats2.txt cats.txt cities d1 d2 d3

移动一个文件

有时,您可能希望将文件(或目录)移动到不同的位置,而不是复制并浪费磁盘空间。

为此,您可以使用mv命令。例如,您可以通过运行mv copycats.txt /tmp命令,将文件copycats.txt从 Elliot 的主目录移动到/tmp

elliot@ubuntu-linux:~$ mv copycats.txt /tmp
elliot@ubuntu-linux:~$ ls
apple.txt   carrot.txt cities d2  Desktop  Downloads
banana.txt  cats.txt   d1     d3  dir1     Pictures
elliot@ubuntu-linux:~$ cd /tmp
elliot@ubuntu-linux:/tmp$ ls
apple.txt  carrot.txt cats.txt copycats.txt d2
banana.txt cats2.txt  cities   d1           d3

请注意,copycats.txt现在已经从 Elliot 的主目录中消失,因为它已经迁移到/tmp中。

移动多个文件

您也可以像复制多个文件一样移动多个文件。例如,您可以将三个文件apple.txtbanana.txtcarrot.txt/tmp移动到/home/elliot/d1,方法如下:

elliot@ubuntu-linux:/tmp$ mv apple.txt banana.txt carrot.txt /home/elliot/d1
elliot@ubuntu-linux:/tmp$ ls
cats2.txt cats.txt cities copycats.txt d1 d2 d3
elliot@ubuntu-linux:/tmp$ cd /home/elliot/d1
elliot@ubuntu-linux:~/d1$ ls
apple.txt banana.txt carrot.txt
elliot@ubuntu-linux:~/d1$

如您所见,三个文件apple.txtbanana.txtcarrot.txt不再位于/tmp中,因为它们都移动到了/home/elliot/d1。一般来说,mv命令遵循以下语法:

mv source_file(s) destination

移动一个目录

您还可以使用mv命令移动目录。例如,如果要移动目录d3并将其放入d2中,则可以运行mv d3 d2命令:

elliot@ubuntu-linux:~$ mv d3 d2
elliot@ubuntu-linux:~$ cd d2
elliot@ubuntu-linux:~/d2$ ls 
d3
elliot@ubuntu-linux:~/d2$

请注意,移动目录不需要使用递归的-r选项。

移动多个目录

您还可以一次移动多个目录。为了演示,在 Elliot 的主目录中创建一个名为big的目录:

elliot@ubuntu-linux:~$ mkdir big

现在您可以将三个目录d1d2cities移动到big目录中,方法如下:

elliot@ubuntu-linux:~$ mv d1 d2 cities big
elliot@ubuntu-linux:~$ ls big
cities d1 d2
elliot@ubuntu-linux:~$

重命名文件

您还可以使用mv命令重命名文件。例如,如果要将文件cats.txt重命名为dogs.txt,可以运行mv cats.txt dogs.txt命令:

elliot@ubuntu-linux:~$ mv cats.txt dogs.txt
elliot@ubuntu-linux:~$ cat dogs.txt
I love cars!
I love cats!
I love penguins!
elliot@ubuntu-linux:~$

如果要将目录big重命名为small,可以运行mv big small命令:

elliot@ubuntu-linux:~$ mv big small
elliot@ubuntu-linux:~$ ls small 
cities d1 d2
elliot@ubuntu-linux:~$

总之,这就是mv命令的工作原理:

  1. 如果目标目录存在,mv命令将移动源文件到目标目录。

  2. 如果目标目录不存在,mv命令将重命名源文件。

请记住,您一次只能重命名一个文件(或一个目录)。

隐藏文件

您可以通过将文件重命名为以点开头的名称来隐藏任何文件。

让我们试试吧;您可以通过将文件重命名为.dogs.txt来隐藏文件dogs.txt,如下所示:

elliot@ubuntu-linux:~$ ls
apple.txt banana.txt carrot.txt dogs.txt Desktop dir1 small
elliot@ubuntu-linux:~$ mv dogs.txt .dogs.txt
elliot@ubuntu-linux:~$ ls
apple.txt banana.txt carrot.txt Desktop dir1 small
elliot@ubuntu-linux:~$

正如您所看到的,文件dogs.txt现在被隐藏了,因为它被重命名为.dogs.txt。您可以通过重命名它并删除文件名前面的点来取消隐藏.dogs.txt

elliot@ubuntu-linux:~$ mv .dogs.txt dogs.txt
elliot@ubuntu-linux:~$ ls
apple.txt banana.txt carrot.txt dogs.txt Desktop dir1 small
elliot@ubuntu-linux:~$

是的,先生!您也可以以相同的方式隐藏和取消隐藏目录。我会留下这个让你作为练习。

删除文件

您可以使用rm命令来删除文件。例如,如果要删除文件dogs.txt,可以运行rm dogs.txt命令:

elliot@ubuntu-linux:~$ ls
apple.txt banana.txt carrot.txt dogs.txt Desktop dir1 small
elliot@ubuntu-linux:~$ rm dogs.txt
elliot@ubuntu-linux:~$ ls
apple.txt banana.txt carrot.txt Desktop dir1 small

您也可以一次删除多个文件。例如,您可以通过运行rm apple.txt banana.txt carrot.txt命令来删除三个文件apple.txtbanana.txtcarrot.txt

elliot@ubuntu-linux:~$ rm apple.txt banana.txt carrot.txt
elliot@ubuntu-linux:~$ ls
Desktop dir1 small 
elliot@ubuntu-linux:~$

删除目录

您可以通过传递递归的-r选项来删除目录的rm命令。为了演示,让我们首先在 Elliot 的主目录中创建一个名为garbage的目录:

elliot@ubuntu-linux:~$ mkdir garbage
elliot@ubuntu-linux:~$ ls
Desktop dir1 garbage small

现在让我们尝试删除garbage目录:

elliot@ubuntu-linux:~$ rm garbage
rm: cannot remove 'garbage': Is a directory
elliot@ubuntu-linux:~$

糟糕!我出错了,因为我没有传递递归的-r选项。这次我会传递递归选项:

elliot@ubuntu-linux:~$ rm -r garbage
elliot@ubuntu-linux:~$ ls
Desktop dir1 small

太棒了!我们摆脱了garbage目录。

你也可以使用rmdir命令来删除只有空目录。为了演示,让我们创建一个名为garbage2的新目录,并在其中创建一个名为old的文件:

elliot@ubuntu-linux:~$ mkdir garbage2
elliot@ubuntu-linux:~$ cd garbage2
elliot@ubuntu-linux:~/garbage2$ touch old

现在让我们回到 Elliot 的主目录,并尝试使用rmdir命令删除garbage2

elliot@ubuntu-linux:~/garbage2$ cd ..
elliot@ubuntu-linux:~$ rmdir garbage2
rmdir: failed to remove 'garbage2': Directory not empty

正如您所看到的,它不允许您删除非空目录。因此,让我们删除garbage2中的文件old,然后重新尝试删除garbage2

elliot@ubuntu-linux:~$ rm garbage2/old
elliot@ubuntu-linux:~$ rmdir garbage2
elliot@ubuntu-linux:~$ ls
Desktop dir1 small 
elliot@ubuntu-linux:~$

哇!garbage2目录永远消失了。这里要记住的一件事是,rm -r命令将删除任何目录(空目录和非空目录)。另一方面,rmdir命令只会删除空目录。

在本章的最后一个示例中,让我们创建一个名为garbage3的目录,然后在其中创建两个文件a1.txta2.txt

elliot@ubuntu-linux:~$ mkdir garbage3
elliot@ubuntu-linux:~$ cd garbage3/
elliot@ubuntu-linux:~/garbage3$ touch a1.txt a2.txt
elliot@ubuntu-linux:~/garbage3$ ls
a1.txt a2.txt

现在让我们回到 Elliot 的主目录,尝试删除garbage3

elliot@ubuntu-linux:~/garbage3$ cd ..
elliot@ubuntu-linux:~$ rmdir garbage3
rmdir: failed to remove 'garbage3': Directory not empty
elliot@ubuntu-linux:~$ rm -r garbage3
elliot@ubuntu-linux:~$ ls
Desktop dir1 Downloads Pictures small
elliot@ubuntu-linux:~$

正如您所看到的,rmdir命令未能删除非空目录garbage3,而rm -r命令成功删除了它。

没有什么比一个好的知识检查练习更能让信息牢固地留在你的脑海中了。

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 在您的主目录中创建三个文件hacker1hacker2hacker3

  2. 在您的主目录中创建三个目录LinuxWindowsMac

  3. 在您在任务 2 中创建的Linux目录中创建一个名为cool的文件。

  4. 在您在任务 2 中创建的Windows目录中创建一个名为boring的文件。

  5. 在您在任务 2 中创建的Mac目录中创建一个名为expensive的文件。

  6. 将两个文件hacker1hacker2复制到/tmp目录。

  7. 将两个目录WindowsMac复制到/tmp目录。

  8. 将文件hacker3移动到/tmp目录。

  9. 将目录Linux移动到/tmp目录。

  10. 从您的主目录中的Mac目录中删除文件expensive

  11. 从您的主目录中删除目录Mac

  12. 从您的主目录中删除目录Windows

  13. 从您的主目录中删除文件hacker2

  14. 将文件hacker1重命名为hacker01

真或假

  1. cp命令可以复制目录,而不使用递归选项-r

  2. 在移动目录时,您必须使用递归选项-r

  3. 您可以使用mv命令来重命名文件或目录。

  4. 您可以使用rmdir命令删除非空目录。

  5. 您可以使用rm -r命令删除非空目录。

阅读你的手册!

你现在可能会对自己说:“Linux 太难了!有很多命令,甚至有更多的命令选项!我不可能掌握所有这些命令并记住它们。”如果这是你的想法,相信我,你是聪明的。记住所有存在的 Linux 命令是不可能的,即使是最有经验的 Linux 管理员也永远不可能记住所有命令,甚至连 Linus Torvalds 本人也不可能!

那么等等?如果是这样,那么解决方案是什么呢?答案就在美丽的 Linux 文档世界中。Linux 有非常完善的文档,以至于很难在其中迷失。Linux 中有各种工具,不仅可以帮助你记住命令,还可以帮助你理解如何使用它们。

在我的职业生涯中遇到了许多 Linux 专业人士,我注意到最熟练的 Linux 管理员不是那些记住了所有命令的人,而是那些知道如何充分利用 Linux 文档的人。女士们先生们,我强烈建议你系好安全带,仔细阅读本章。我向你保证,你心中的恐惧很快就会消失!

第六章:Linux 命令的四个类别

所有 Linux 命令必须属于以下四个类别中的一个:

  1. 可执行程序:通常是用 C 编程语言编写的。cp命令就是一个可执行命令的例子。

  2. 别名:基本上是命令(或一组命令)的另一个名称。

  3. shell 内置命令:shell 也支持内部命令。exitcd命令就是 shell 内置命令的两个例子。

  4. shell 函数:这些函数帮助我们完成特定任务,在编写 shell 脚本时至关重要。稍后我们会更详细地介绍这个,现在只需要知道它们存在即可。

确定命令的类型

你可以使用type命令来确定命令的类型(类别)。例如,如果你想知道pwd命令的类型,只需运行type pwd命令:

elliot@ubuntu-linux:~$ type pwd 
pwd is a shell builtin

所以现在你知道pwd命令是一个 shell 内置命令。现在让我们弄清楚ls命令的类型:

elliot@ubuntu-linux:~$ type ls
ls is aliased to `ls --color=auto'

你可以看到,ls命令被别名为ls --color=auto。现在你知道为什么每次运行ls命令时都会看到彩色的输出了。让我们看看date命令的类型:

elliot@ubuntu-linux:~$ type date 
date is /bin/date

任何位于/bin/sbin中的命令都是可执行程序。因此,我们可以得出date命令是一个可执行程序,因为它位于/bin中。

最后,让我们确定type命令本身的类型:

elliot@ubuntu-linux:~$ type type 
type is a shell builtin

原来type命令是一个 shell 内置命令。

查找命令的位置

每次运行一个可执行命令时,系统中都会有一个文件被执行。你可以使用which命令来确定可执行命令的位置。例如,如果你想知道rm命令的位置,可以运行which rm命令:

elliot@ubuntu-linux:~$ which rm
/bin/rm

所以现在你知道rm位于/bin目录中。让我们看看reboot命令的位置:

elliot@ubuntu-linux:~$ which reboot
/sbin/reboot

你可以看到,reboot命令位于/sbin目录中。

这个命令是做什么的?

你可以使用whatis命令来获取一个命令的简要描述。例如,如果你想知道free命令的目的,可以运行whatis free命令:

elliot@ubuntu-linux:~$ whatis free
free (1)             - Display amount of free and used memory in the system

你可以看到,free命令,正如我们已经知道的那样,显示系统中的空闲和已使用内存量。酷!现在让我们看看df命令的作用:

elliot@ubuntu-linux:~$ whatis df
df (1)               - report file system disk space usage

最后,让我们看看which命令的作用:

elliot@ubuntu-linux:~$ whatis which 
which (1)            - locate a command

正如我们已经知道的那样,which显示了一个命令的位置。

man 页面

whatis命令给出了一个命令的简要描述;然而,它并不教你如何使用一个命令。为此,你可以使用man页面。

man页面是一个手册页面,其中有适当的文档,可帮助您了解如何使用命令。就像您购买新手机时,会得到一本手册,告诉您如何使用手机以及如何在手机上更新软件等。

一般来说,如果要阅读命令的man页面,可以运行:

man command_name

例如,如果要查看touch命令的man页面,可以运行man touch命令:

elliot@ubuntu-linux:~$ man touch

图 1:touch man 页面

正如您在前面的屏幕截图中看到的,touch man 页面显示了如何使用该命令,并列出并解释了所有命令选项。

表 9向您展示了在浏览man页面时如何移动。

man 键 它的作用
空格 向前滚动一页。
Ctrl+F 向前滚动一页(与空格相同)。
Ctrl+B 向后滚动一页。
/word 将在man页面中搜索单词(模式)。例如,/access将在man页面中搜索单词access
q 将退出man页面。
n 在搜索单词后,您可以使用n来查找man页面中单词的下一个出现。
N 在搜索单词后,您可以使用N来查找man页面中单词的上一个出现。

我无法再次强调man页面的重要性。相信我,在最黑暗的时刻,它们可以成为您最好的朋友!

您还应该知道man本身有一个 man 页面:

elliot@ubuntu-linux:~$ man man

它描述了如何使用man页面。

shell 内置命令的帮助

如果您足够玩转man页面,您可能会注意到许多 shell 内置命令没有man页面。例如,cdexit命令没有man页面:

elliot@ubuntu-linux:~$ type cd 
cd is a shell builtin 
elliot@ubuntu-linux:~$ man cd 
No manual entry for cd 
elliot@ubuntu-linux:~$ type exit 
exit is a shell builtin 
elliot@ubuntu-linux:~$ man exit 
No manual entry for exit

这是因为 shell 内置命令没有man页面,但不要慌!您仍然可以使用help命令找到如何使用 shell 内置命令的帮助。例如,要获取有关如何使用exit命令的帮助,您可以运行:

elliot@ubuntu-linux:~$ help exit 
exit: exit [n]
 Exit the shell.

 Exits the shell with a status of N. If N is omitted, the exit status 
 is that of the last command executed.

类似地,要获取有关如何使用cd命令的帮助,您可以运行help cd命令:

elliot@ubuntu-linux:~$ help cd 
cd: cd [-L|-P] [dir]
 Change the shell working directory.

 Change the current directory to DIR. The default DIR is the value of 
 the HOME shell variable.

 The variable CDPATH defines the search path for the directory containing DIR. 
 Alternative directory names in CDPATH are separated by a colon (:). 
 A null directory name is the same as the current directory. 
 If DIR begins with a slash (/), then CDPATH is not used.

 If the directory is not found, and the shell option `cdable_vars' is set, 
 the word is assumed to be a variable name. If that variable has a value, 
 its value is used for DIR.

 Options:
 -L force symbolic links to be followed
 -P use the physical directory structure without following symbolic links
 The default is to follow symbolic links, as if `-L' were specified. 

 Exit Status:
 Returns 0 if the directory is changed; non-zero otherwise.

信息页面

GNU 项目推出了info页面,作为man页面的替代文档。GNU 项目曾声称man页面已过时,需要替换,因此他们推出了info页面。

您可以通过运行以下命令查看任何命令的info页面:

info command_name

例如,要查看ls命令的info页面,可以运行info ls命令:

elliot@ubuntu-linux:~$ info ls

Next: dir invocation, Up: Directory listing

10.1 ‘ls': List directory contents
==================================

The ‘ls' program lists information about files (of any type, including directories). Options and file arguments can be intermixed arbitrarily, as usual.

For non-option command-line arguments that are directories, by default ‘ls' lists the contents of directories, not recursively, and omitting files with names beginning with ‘.'. For other non-option arguments, by default ‘ls' lists just the file name. If no non-option argument is specified, ‘ls' operates on the current directory, acting as if it had been invoked with a single argument of ‘.'.

By default, the output is sorted alphabetically, according to the locale settings in effect.(1) If standard output is a terminal, the output is in columns (sorted vertically) and control characters are output as question marks; otherwise, the output is listed one per line and control characters are output as-is.

Because ‘ls' is such a fundamental program, it has accumulated many options over the years. They are described in the subsections below; within each section, options are listed alphabetically (ignoring case). The division of options into the subsections is not absolute, since some options affect more than one aspect of ‘ls''s operation.

info页面有时提供比man页面更详细的信息。但是,man页面仍然是 Linux 上帮助文档的最受欢迎的去处。

非常有帮助的 apropos 命令

apropos命令是最有帮助但却被低估的 Linux 命令之一。让我们看一下apropos命令的简要描述:

elliot@ubuntu-linux:~$ whatis apropos
apropos (1)          - search the manual page names and descriptions

哇!apropos命令帮助您搜索正确的命令以实现特定任务。例如,假设您想重命名文件,但不确定要使用哪个 Linux 命令;在这种情况下,您可以运行apropos rename命令:

elliot@ubuntu-linux:~$ apropos rename
file-rename (1p)     - renames multiple files
File::Rename (3pm)   - Perl extension for renaming multiple files 
gvfs-rename (1)      - (unknown subject)
mmove (1)            - move or rename an MSDOS file or subdirectory 
mren (1)             - rename an existing MSDOS file
mv (1)               - move (rename) files 
prename (1p)         - renames multiple files 
rename (1)           - renames multiple files
rename.ul (1)        - rename files

轰隆!它列出了所有具有rename一词显示在其 man 页面描述中的命令。我打赌您可以在输出中找到mv命令。

假设您想查看日历,但不确定要使用哪个命令;在这种情况下,您可以运行:

elliot@ubuntu-linux:~$ apropos calendar
cal (1)              - displays a calendar and the date of Easter
calendar (1)         - reminder service
ncal (1)             - displays a calendar and the date of Easter

您可以看到它在输出中显示了cal命令。

对于最后一个例子,假设您想显示 CPU 信息,但不知道要使用哪个命令;在这种情况下,您可以运行:

elliot@ubuntu-linux:~$ apropos cpu 
chcpu (8)            - configure CPUs
cpuid (4)            - x86 CPUID access device
cpuset (7)           - confine processes to processor and memory node subsets 
lscpu (1)            - display information about the CPU architecture
msr (4)              - x86 CPU MSR access device
sched (7)            - overview of CPU scheduling
taskset (1)          - set or retrieve a process's CPU affinity

就是这样!您可以看到它列出了我们之前使用过的lscpu命令。每当您忘记一个命令或不确定要使用哪个命令时,apropos命令就在这里拯救您。您只需向apropos命令提供一个关键词(最好是动词),以突出您想要完成的任务:

apropos keyword

酷技巧

man -k命令将显示与apropos命令相同的结果。

elliot@ubuntu-linux:~$ man -k cpu 
chcpu (8)            - configure CPUs
cpuid (4)            - x86 CPUID access device
cpuset (7)           - confine processes to processor and memory node subsets 
lscpu (1)            - display information about the CPU architecture
msr (4)              - x86 CPU MSR access device
sched (7)            - overview of CPU scheduling
taskset (1)          - set or retrieve a process's CPU affinity

/usr/share/doc目录

/usr/share/doc目录是在 Linux 中寻求帮助的另一个绝佳地方。这个目录有非常详尽的文档;它不仅仅向你展示如何使用一个命令;有时甚至会显示开发该命令的作者的姓名和联系信息。此外,它还可能包括一个TODO文件,其中包含一个未完成的任务/功能列表;贡献者通常会查看TODO文件来帮助修复错误和开发新功能。

为了演示,让我们去nano文档目录:

elliot@ubuntu-linux:~$ cd /usr/share/doc/nano 
elliot@ubuntu-linux:/usr/share/doc/nano$ pwd
/usr/share/doc/nano

现在列出目录的内容,看看里面有什么:

elliot@ubuntu-linux:/usr/share/doc/nano$ ls
AUTHORS               copyright faq.html        nano.html   README    TODO 
changelog.Debian.gz   examples  IMPROVEMENTS.gz NEWS.gz     THANKS.gz

太棒了!你可以查看AUTHORS文件,看看谁贡献了nano编辑器程序的开发团队。你还可以查看TODO文件,如果你渴望知道是否还有什么事情要做!你还可以查看README文件,了解nano编辑器的一般描述。甚至还有一个包含常见问题的faq.html链接。

正如你在本章中看到的,Linux 有各种有用的工具可供你使用;所以确保你充分利用它们!

知识检测

对于以下练习,打开你的终端并尝试解决以下任务:

  1. 你需要知道echo命令是一个 shell 内置命令还是可执行程序,你会运行哪个命令?

  2. 显示uptime命令可执行文件的位置。

  3. 显示mkdir命令的简要描述。

  4. 你忘记了如何使用mv命令,你打算怎么办?

  5. 你忘记了用来显示日历的命令,你打算怎么办?

  6. history命令是一个 shell 内置命令,因此它没有 man 页面。你想要清除你的历史记录,但不知道该怎么做。你打算怎么办?

真或假

  1. whereis命令用于定位命令。

  2. 你可以互换使用man -papropos

  3. 你可以使用whatis命令来获取一个命令的简要描述。

  4. 你可以使用type命令来确定一个命令是别名、shell 内置命令还是可执行程序。

硬链接与软链接

在本章中,我们进一步了解 Linux 文件,并讨论硬链接和软链接之间的区别。如果您以前在 Windows(或 macOS)中创建过快捷方式,您将很快掌握软链接的概念。但在讨论硬链接和软链接之前,您首先必须了解 inode 的概念。

第七章:文件 inode

当您去杂货店时,您会发现每种产品都有一组属性,例如:

  • 产品类型:巧克力

  • 产品价格:$2.50

  • 产品供应商:Kit Kat

  • 剩余金额:199

这些属性可以通过扫描产品的条形码在杂货店的任何产品上显示。当然,每个条形码都是唯一的。嗯,您可以将这个类比应用到 Linux。Linux 上的每个文件都有一组属性,例如:

  • 文件类型

  • 文件大小

  • 文件所有者

  • 文件权限

  • 硬链接数量

  • 文件时间戳

这些属性存储在称为 inode(索引节点)的数据结构中,每个 inode 由一个编号(inode 编号)标识。因此,您可以将 inode 编号视为杂货店中的条形码。Linux 上的每个文件都有一个 inode 编号,每个 inode 编号指向一个文件数据结构,即 inode。以下是 inode 的正式定义:

什么是 inode?

inode 只是一个存储文件信息(属性)的文件数据结构,并且每个 inode 都由一个编号(inode 编号)唯一标识。

显示文件 inode 编号

有两个命令可以用来查看文件的 inode 编号:

  1. ls -i文件

  2. stat文件

例如,要查看facts.txt的 inode 编号,您可以运行ls -i facts.txt命令:

elliot@ubuntu-linux:~$ ls -i facts.txt 
924555 facts.txt

它将为您输出 inode 编号。您还可以使用stat命令:

elliot@ubuntu-linux:~$ stat facts.txt 
File: facts.txt
Size: 173 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 924555 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ tom) Gid: ( 1000/ tom) 
Access: 2019-05-08 13:41:16.544000000 -0600
Modify: 2019-05-08 12:50:44.112000000 -0600
Change: 2019-05-08 12:50:44.112000000 -0600
Birth: -

stat命令不仅列出文件的 inode 编号;它还列出所有文件属性,正如您从命令输出中看到的那样。

创建软链接

现在,既然您了解了文件 inode 是什么,您可以轻松理解硬链接和软链接的概念。让我们从软链接开始:

什么是软链接?

软链接(也称为符号链接)只是指向另一个文件的文件。

一图胜过千言万语,因此以下图表将帮助您可视化软链接。

图 1:软链接可视化

要创建软链接,我们使用ln命令和-s选项,如下所示:

ln -s original_file soft_link

因此,要创建名为soft.txt的软链接到facts.txt文件,您可以运行ln -s facts.txt soft.txt命令:

elliot@ubuntu-linux:~$ ln -s facts.txt soft.txt

现在让我们对刚刚创建的软链接文件soft.txt进行长列表:

elliot@ubuntu-linux:~$ ls -l soft.txt
lrwxrwxrwx 1 tom tom 9 May 8 21:48 soft.txt -> facts.txt

您会注意到两件事。首先,输出的第一列中的字母l,表示文件是一个链接(软链接),其次您可以看到右箭头soft.txt → facts.txt,这基本上告诉我们soft.txt是一个指向文件facts.txt的软链接。

现在让我们检查文件soft.txt的内容:

elliot@ubuntu-linux:~$ cat soft.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.

当然,它包含与原始文件facts.txt相同的数据。实际上,如果您编辑软链接,它实际上也会编辑原始文件。

为了演示,用任何文本编辑器打开文件soft.txt,并在文件的最末尾添加一行“草是绿色的。”,然后保存并退出,这样soft.txt的内容将如下所示:

elliot@ubuntu-linux:~$ cat soft.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.

现在让我们检查原始文件facts.txt的内容:

elliot@ubuntu-linux:~$ cat facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.

正如您所看到的,新行“草是绿色的。”也在那里。这是因为每次您编辑软链接时,它实际上也会编辑指向的原始文件。

现在,如果您删除软链接,原始文件不会受到任何影响,它仍然完好无损:

elliot@ubuntu-linux:~$ rm soft.txt 
elliot@ubuntu-linux:~$ cat facts.txt
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.

现在让我们再次创建软链接soft.txt

elliot@ubuntu-linux:~$ ln -s facts.txt soft.txt

如果您删除原始文件facts.txt,软链接soft.txt将变得无用!但在删除facts.txt文件之前,让我们在/tmp中制作一个副本,因为以后我们会需要它:

elliot@ubuntu-linux:~$ cp facts.txt /tmp

现在让我们从elliot的主目录中删除文件facts.txt,看看软链接会发生什么:

elliot@ubuntu-linux:~$ rm facts.txt 
elliot@ubuntu-linux:~$ cat soft.txt 
cat: soft.txt: No such file or directory

如您所见,软链接soft.txt变得无用,因为它现在指向无处。请记住,文件soft.txt仍然存在,如下截图所示。

图 2:soft.txt 变得无用!

以下图表向您展示了原始文件facts.txt被删除后,软链接soft.txt指向无处。

图 3:soft.txt 指向无处

现在,如果我们将facts.txt移回elliot的主目录:

elliot@ubuntu-linux:~$ mv /tmp/facts.txt /home/elliot

软链接soft.txt将再次有用!您可以说我们复活了软链接!

elliot@ubuntu-linux:~$ cat soft.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.

让我们比较软链接soft.txt和原始文件facts.txt的 inode 号:

elliot@ubuntu-linux:~$ ls -i soft.txt facts.txt 
925155 facts.txt 924556 soft.txt

如您所见,两个文件的 inode 号是不同的。最后,让我们对软链接soft.txt运行stat命令:

elliot@ubuntu-linux:~$ stat soft.txt 
File: soft.txt -> facts.txt
Size: 9 Blocks: 0 IO Block: 4096 symbolic link
Device: 801h/2049d Inode: 924556 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 1000/ tom) Gid: ( 1000/ tom) 
Access: 2019-05-08 22:04:58.636000000 -0600
Modify: 2019-05-08 22:02:18.356000000 -0600
Change: 2019-05-08 22:02:18.356000000 -0600
Birth: -

如您所见,它将文件列为符号链接,这是软链接的另一个名称。

因此,正如您迄今所见,软链接具有以下属性:

  • 软链接的 inode 与原始文件不同。

  • 一旦原始文件被删除,软链接就变得无用。

  • 对软链接的任何更改实际上都是对原始文件的更改。

  • 您可以创建对目录的软链接。

您可以创建对目录的软链接,就像您可以创建对文件的软链接一样。为了演示,让我们首先在elliot的主目录中创建一个名为sports的目录。并在sports中创建三个文件-swimmingsoccerhockey,如下所示:

elliot@ubuntu-linux:~$ mkdir sports
elliot@ubuntu-linux:~$ touch sports/swimming sports/soccer sports/hockey 
elliot@ubuntu-linux:~$ ls sports
hockey soccer swimming

现在让我们创建名为softdir1的软链接到sports目录:

elliot@ubuntu-linux:~$ ln -s sports softdir1

现在如果您切换到softdir1,实际上是切换到sports,因此您将看到相同的目录内容:

elliot@ubuntu-linux:~$ cd softdir1 
elliot@ubuntu-linux:~/softdir1$ ls 
hockey soccer swimming

当然,对目录也是一样的;也就是说,如果您删除原始目录,软链接将变得无用!

创建硬链接

当涉及到硬链接时,情况有些不同。这是因为硬链接是原始文件的副本。以下是硬链接的定义:

什么是硬链接?

硬链接只是现有文件的附加名称。它具有与原始文件相同的 inode,因此与原始文件无法区分。

您可以将其视为昵称。当有人用您的昵称称呼您时,他们仍然在指代您。

硬链接具有以下属性:

  • 硬链接具有与原始文件相同的 inode(共享)。

  • 如果原始文件被删除,硬链接仍然保持完整。

  • 对硬链接的任何更改都会反映在原始文件中。

  • 您无法创建对目录的硬链接。

以下图表可帮助您可视化硬链接:

图 4:硬链接可视化

我们使用相同的ln命令来创建硬链接,但这次我们省略了-s选项:

ln original_file hard_link

因此,要创建名为hard.txt的硬链接到文件facts.txt,您只需运行命令ln facts.txt hard.txt

elliot@ubuntu-linux:~$ ln facts.txt hard.txt

现在让我们对硬链接hard.txt和原始文件facts.txt进行长列表:

elliot@ubuntu-linux:~$ ls -l hard.txt
-rw-rw-r-- 2 tom tom 210 May 9 00:07 hard.txt 
elliot@ubuntu-linux:~$ ls -l facts.txt
-rw-rw-r-- 2 tom tom 210 May 9 00:07 facts.txt

它们是相同的!硬链接也与原始文件一样具有相同的内容:

elliot@ubuntu-linux:~$ cat hard.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.

现在使用您选择的文本编辑器向硬链接hard.txt的末尾添加一行“游泳是一项运动。”:

elliot@ubuntu-linux:~$ cat hard.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.
Swimming is a sport.

现在就像软链接的情况一样,原始文件的内容也发生了变化:

elliot@ubuntu-linux:~$ cat facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.
Swimming is a sport.

现在让我们检查两个文件的 inode 号:

elliot@ubuntu-linux:~ ls -i hard.txt facts.txt 
925155 facts.txt 925155 hard.txt

请注意,两个文件具有相同的 inode 号。现在让我们对两个文件运行stat命令:

elliot@ubuntu-linux:~$ stat hard.txt facts.txt 
File: hard.txt
Size: 210 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 925155 Links: 2
Access: (0664/-rw-rw-r--) Uid: ( 1000/ elliot) Gid: ( 1000/ elliot) 
Access: 2019-05-09 00:07:36.884000000 -0600
Modify: 2019-05-09 00:07:25.708000000 -0600
Change: 2019-05-09 00:07:25.720000000 -0600
Birth: -
File: facts.txt
Size: 210 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 925155 Links: 2
Access: (0664/-rw-rw-r--) Uid: ( 1000/ elliot) Gid: ( 1000/ elliot)
Access: 2019-05-09 00:07:36.884000000 -0600
Modify: 2019-05-09 00:07:25.708000000 -0600
Change: 2019-05-09 00:07:25.720000000 -0600
Birth: -

stat命令的输出对两个文件都是相同的。而且,这里的链接数:2表示有两个硬链接指向该文件。嗯!我们只创建了一个硬链接指向文件facts.txt,那么为什么会列出两个硬链接呢?原来,原始文件是指向自身的硬链接,所以任何文件至少有一个硬链接(指向自身)。

现在与软链接的情况不同,如果你删除原始文件facts.txt

elliot@ubuntu-linux:~$ rm facts.txt

硬链接保持不变:

elliot@ubuntu-linux:~$ cat hard.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.
Swimming is a sport.

下图显示了为什么硬链接保持不变。

图 5:hard.txt 保持不变

现在注意,在删除文件facts.txt后,文件hard.txt的硬链接计数将减少到一个:

elliot@ubuntu-linux:~$ stat hard.txt 
File: hard.txt
Size: 210 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 925155 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ elliot) Gid: ( 1000/ elliot) 
Access: 2019-05-09 00:17:21.176000000 -0600
Modify: 2019-05-09 00:07:25.708000000 -0600
Change: 2019-05-09 00:17:18.696000000 -0600
Birth: -

你不能创建一个指向目录的硬链接。如果你不相信我,那就试着创建一个名为variables的硬链接指向/var目录:

elliot@ubuntu-linux:~$ ln /var variables
ln: /var: hard link not allowed for directory

我告诉过你,目录不允许有硬链接!你为什么怀疑我?

令人震惊的事实

没有办法区分原始文件和硬链接。例如,如果给你两个文件,其中一个恰好是另一个文件的硬链接,那么没有办法知道哪个文件是原始文件!这就像鸡和蛋的困境;没有人知道哪个先出现!

知识检测

对于以下练习,打开你的终端并尝试解决以下任务:

  1. 显示/var/log目录的 inode 编号。

  2. 显示/boot目录的硬链接数。

  3. 在你的主目录中创建一个名为coins的新目录。

  4. 创建一个指向coins的软链接,名为currency

  5. coins目录中,创建两个文件——silvergold

  6. currency目录中创建一个新文件bronze

  7. 列出coinscurrency两个目录的内容。

  8. 在你的主目录中创建一个包含“咖啡很棒”的新文件beverages,并创建一个名为drinks的硬链接指向beverages

  9. drinks文件中添加一行“柠檬很清爽”,然后删除beverages文件。

  10. 显示你的drinks文件的内容。

真或假

  1. 文件名是 inode 数据结构的一部分。

  2. 文件大小是 inode 数据结构的一部分。

  3. 你可以创建指向目录的软链接。

  4. 你可以创建指向目录的硬链接。

  5. 目录的最小硬链接数为2

  6. 软链接与原始文件具有相同的 inode 编号。

  7. 硬链接与原始文件具有相同的 inode 编号。

谁是 root?

到目前为止,用户elliot已经能够在系统上做了很多事情。但是,有很多事情用户elliot无法做!为了演示,让我们尝试在/var目录中创建一个名为happy的文件:

elliot@ubuntu-linux:~$ touch /var/happy
touch: cannot touch '/var/happy': Permission denied

哎呀!我们得到了Permission denied错误。

现在让我们尝试在/etc中创建名为games的新目录:

elliot@ubuntu-linux:/$ mkdir /etc/games
mkdir: cannot create directory ‘/etc/games': Permission denied

再次!我们得到了相同的错误,Permission denied

这里发生了什么?嗯,用户elliot没有权限在系统上做任何他想做的事情!那么谁?谁有权限在系统上做任何事情?是 root 用户。

谁是 root?

root是具有在系统上执行任何操作权限的 Linux 用户。root也被称为超级用户。

第八章:访问 root 用户

您可以运行sudo -i命令首次访问系统上的root用户:

elliot@ubuntu-linux:~$ sudo -i
[sudo] password for elliot:
root@ubuntu-linux:~#

您将被提示输入密码,然后突然之间,您拥有了超级权限!

注意命令提示符的变化,而不是美元符号($),它现在显示#来欢迎 root 用户。

让我们运行whoami命令,确保我们现在已登录为 root 用户:

root@ubuntu-linux:~# whoami 
root

太棒了!现在让我们显示当前工作目录:

root@ubuntu-linux:~# pwd
/root

记得之前我告诉过你,root用户的主目录是/root而不是在/home下。

图 1:/root 是 root 用户的主目录

现在让我们重新运行我们两次被拒绝的命令,但这次,我们以root用户身份运行两个命令。

root@ubuntu-linux:~# touch /var/happy 
root@ubuntu-linux:~# ls -l /var/happy
-rw-r--r-- 1 root root 0 Apr 15 10:53 /var/happy

正如您所看到的,没有什么可以阻止root用户做任何事情!现在让我们在/etc中创建目录games

root@ubuntu-linux:~# mkdir /etc/games 
root@ubuntu-linux:~# ls -ld /etc/games
drwxr-xr-x 2 root root 4096 Apr 15 10:55 /etc/games

我们没有错误,这是因为您作为root用户有权进行任何操作。但是请永远记住,伴随着强大的力量而来的是巨大的责任。

设置 root 密码

您还可以使用su命令切换到root用户,但首先,您需要设置root的密码:

root@ubuntu-linux:~# passwd 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

太棒了,现在退出root用户:

root@ubuntu-linux:~# exit 
logout
elliot@ubuntu-linux:~$ whoami 
elliot

现在您可以使用su root命令切换到root用户:

elliot@ubuntu-linux:~$ su root 
Password:
root@ubuntu-linux:/home/elliot# whoami 
root

破折号的区别

请注意,我的当前工作目录现在是/home/elliot而不是/root。如果我想更改,我可以退出到用户elliot,然后重新运行su命令,但这次,在用户名之前加上破折号(连字符)。

root@ubuntu-linux:/home/elliot# exit 
exit
elliot@ubuntu-linux:~$ su - root 
Password:
root@ubuntu-linux:~# pwd
/root

那么有什么区别吗?

这是交易。当您在用户名之前不添加破折号时,shell 会保留当前用户的 shell 环境设置,其中包括当前工作目录。另一方面,当您添加破折号时,shell 会获取新用户(您切换到的用户)的环境设置。

所以让我们练习一下。如果您想切换到用户elliot但保留root的 shell 环境设置,则不需要破折号:

root@ubuntu-linux:~# pwd
/root
root@ubuntu-linux:~# su elliot 
elliot@ubuntu-linux:/root$ pwd
/root
elliot@ubuntu-linux:/root$

注意当我切换到用户elliot时,当前工作目录没有更改。现在,让我们退出并再次切换到用户elliot,但这次,在用户名之前加上破折号:

elliot@ubuntu-linux:/root$ exit 
exit
root@ubuntu-linux:~# pwd
/root
root@ubuntu-linux:~# su - elliot 
elliot@ubuntu-linux:~$ pwd
/home/elliot

现在注意当前工作目录如何从/root更改为/home/elliot。因此,在这里,shell 获取了用户elliot的环境设置。

一个很酷的提示

如果您运行su而不指定用户名,则su将切换到 root 用户。因此,如果您想节省一些输入,每次想切换到 root 用户时都可以省略用户名。

让我们尝试一下我们很酷的提示!作为用户elliot,运行su命令而不指定用户名:

elliot@ubuntu-linux:~$ su 
Password:
root@ubuntu-linux:/home/elliot#

然后,您可以输入root密码以登录为root

您还可以使用破折号获取root的 shell 环境设置:

elliot@ubuntu-linux:~$ su - 
Password:
root@ubuntu-linux:~# pwd
/root

这次我降落在/root,因为我使用了破折号。

嗯,这是一个简短的章节,但root用户肯定值得有一个专门的部分。还要记住,当你是root用户时,你拥有超级权限,可以在系统上做任何事情。所以如果你不非常小心,你可能会损坏你的系统,这就是为什么有一个非常著名的 Linux 迷因说,“不要喝酒然后使用 root!”

知识检查

对于以下练习,打开你的终端并尝试解决以下任务:

  1. 切换到root用户。

  2. 更改root用户的密码。

  3. 切换到用户elliot并登陆到/home/elliot

  4. 现在切换到 root 用户,但保留当前工作目录/home/elliot

真或假

  1. root用户是 Linux 中最强大的用户。

  2. 使用su命令而不指定用户名将切换到 root 用户。

  3. 我们使用passroot命令来更改root用户的密码。

控制人口

Linux 是一个多用户操作系统,这意味着许多用户可以同时访问系统。在现实生活中,你几乎不会找到只有一个用户的 Linux 服务器。相反,你会在一个服务器上看到很多用户。所以让我们真实地为我们的系统添加各种用户和组。在本章中,您将学习如何向 Linux 系统添加用户和组。您还将学习如何以各种方式管理用户和组帐户。此外,您还将学习如何管理 Linux 文件权限。

第九章:/etc/passwd 文件

在 Linux 中,用户信息存储在/etc/passwd文件中。/etc/passwd中的每一行都对应于一个用户。当您首次打开/etc/passwd时,您会看到很多用户,然后您会想,这些用户都是从哪里来的?答案很简单:这些用户中的大多数是服务用户,它们由您的系统用于启动各种应用程序和服务。然而,本章的主要重点将是系统用户;这些是像您和我一样的真正的人!

/etc/passwd中的每一行都由 7 个字段组成,每个字段用冒号分隔,每个字段代表一个用户属性。例如,用户elliot的条目看起来可能是这样的:

图 1:/etc/passwd 中的 7 个字段

以下表格详细说明了/etc/passwd中的这七个字段,并解释了每一个:

字段 它存储什么?
1 这个字段存储用户名。
2 这个字段通常有一个X,这意味着用户的密码已加密并存储在文件/etc/shadow中。
3 这个字段存储UID用户 ID)号码。
4 这个字段存储用户的主要GID组 ID)。
5 这个字段存储用户的注释,通常是用户的名字和姓氏。
6 这个字段存储用户的主目录路径。
7 这个字段存储用户的默认 shell。

表 10:理解/etc/passwd

添加用户

在您可以在系统上添加用户之前,您必须成为root

elliot@ubuntu-linux:~$ su - 
Password:
root@ubuntu-linux:~#

现在,我们准备好添加用户了。我们都喜欢汤姆和杰瑞,所以让我们从添加用户tom开始。为此,您需要运行命令useradd -m tom

root@ubuntu-linux:~# useradd -m tom

就像这样,用户tom现在已经添加到我们的系统中。您还会看到在/etc/passwd文件的末尾添加了一个新行,用于新用户tom;让我们用可爱的tail命令查看一下:

root@ubuntu-linux:~# tail -n 1 /etc/passwd 
tom:x:1007:1007::/home/tom:/bin/sh

我们使用useradd命令的-m选项来确保为用户tom创建一个新的主目录。所以让我们尝试切换到/home/tom目录,以确保它确实已经创建:

root@ubuntu-linux:~# cd /home/tom 
root@ubuntu-linux:/home/tom# pwd
/home/tom

太棒了!我们验证了/home/tom已经创建。

在创建新用户后,您可能想要做的第一件事是设置用户的密码。您可以通过运行命令passwd tom来设置tom的密码:

root@ubuntu-linux:~# passwd tom 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

现在,让我们创建用户jerry。但是这次,我们将为用户jerry选择以下属性:

UID 777
注释 Jerry the Mouse
Shell /bin/bash

这很容易通过useradd命令来完成:

root@ubuntu-linux:~# useradd -m -u 777 -c "Jerry the Mouse" -s /bin/bash jerry

-u选项用于设置jerry的 UID。我们还使用了-c选项为用户jerry添加注释,最后我们使用了-s选项为jerry设置默认 shell。

现在,让我们查看/etc/passwd文件的最后两行,进行一些比较:

root@ubuntu-linux:~# tail -n 2 /etc/passwd 
tom:x:1007:1007::/home/tom:/bin/sh 
jerry:x:777:1008:Jerry the Mouse:/home/jerry:/bin/bash

请注意,用户tom的注释字段为空,因为我们在创建用户tom时没有添加任何注释,还要注意用户tom的 UID 是由系统选择的,但我们为用户jerry选择了777。另外,注意用户tom的默认 shell 是由系统选择的/bin/sh,这是/bin/bash的旧版本。然而,我们为用户jerry选择了更新的 shell/bin/bash

现在,让我们为用户jerry设置密码:

root@ubuntu-linux:~# passwd jerry 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

太棒了!我们现在已经创建了两个用户:tomjerry。现在,让我们切换到用户tom

root@ubuntu-linux:~# su - tom
$ whoami tom
$ pwd
/home/tom
$

我们成功切换到了用户tom,但是你可以看到,shell 看起来很不一样,因为命令提示符不显示用户名或主机名。这是因为用户tom的默认 shell 是/bin/sh。你可以使用echo $SHELL命令来显示用户的默认 shell:

$ echo $SHELL
/bin/sh

如你所见,它显示了/bin/sh。现在,让我们退出并切换到用户jerry

$ exit
root@ubuntu-linux:~# su - jerry 
jerry@ubuntu-linux:~$ whoami 
jerry
jerry@ubuntu-linux:~$ echo $SHELL
/bin/bash

一切看起来都更好了,因为我们确实将他的默认 shell 设置为/bin/bash。好了,现在让我们切换回root用户:

jerry@ubuntu-linux:~$ exit 
logout
root@ubuntu-linux:~#

修改用户属性

所以我们不满意用户tom的默认 shell 是/bin/sh,我们想把它改成/bin/bash。我们可以使用usermod命令来修改用户属性。

例如,要将用户tom的默认 shell 更改为/bin/bash,你可以运行命令usermod -s /bin/bash tom

root@ubuntu-linux:~# usermod -s /bin/bash tom

请注意,你也可以使用命令选项的全名;所以你可以使用--shell代替-s。无论如何,让我们看看我们是否成功地更改了用户tom的默认 shell:

root@ubuntu-linux:~# su - tom 
tom@ubuntu-linux:~$ whoami 
tom
tom@ubuntu-linux:~$ echo $SHELL
/bin/bash

太棒了!我们成功了。你也可以通过运行命令usermod -u 444 tomtom的 UID 更改为444

root@ubuntu-linux:~# usermod -u 444 tom

我们确实可以通过查看/etc/passwd文件来检查tom的 UID 是否已更改:

root@ubuntu-linux:~# tail -n 2 /etc/passwd 
tom:x:444:1007::/home/tom:/bin/bash 
jerry:x:777:1008:Jerry the Mouse:/home/jerry:/bin/bash

我们甚至可以修改用户tom的注释字段。现在,它是空的,但你可以通过运行命令将用户tom的注释字段设置为"Tom the Cat"

root@ubuntu-linux:~# usermod --comment "Tom the Cat" tom

而且,我们可以通过查看/etc/passwd文件来验证评论是否已更改:

root@ubuntu-linux:~# tail -n 2 /etc/passwd 
tom:x:444:1007:Tom the Cat:/home/tom:/bin/bash 
jerry:x:777:1008:Jerry the Mouse:/home/jerry:/bin/bash

定义骨架

如果你列出/home/jerry/home/tom的内容,你会发现它们是空的:

root@ubuntu-linux:~# ls -l /home/tom 
total 0
root@ubuntu-linux:~# ls -l /home/jerry 
total 0

/home/jerry/home/tom都是空的原因是骨架文件/etc/skel也是空的:

root@ubuntu-linux:~# ls -l /etc/skel 
total 0

/etc/skel 是什么?

这是骨架文件。在/etc/skel中创建的任何文件或目录都将被复制到任何新创建的用户的主目录中。

现在,用你最喜欢的文本编辑器,在/etc/skel中创建文件welcome.txt,并在其中插入一行"Hello Friend!"

root@ubuntu-linux:/etc/skel# ls 
welcome.txt
root@ubuntu-linux:/etc/skel# cat welcome.txt 
Hello Friend!

好了,现在你已经在/etc/skel中创建了文件welcome.txt,这意味着任何新创建的用户现在都会在他们的主目录中有文件welcome.txt。为了演示,让我们创建一个名为edward的新用户,然后我们将看一下他的主目录:

root@ubuntu-linux:~# useradd -m -c "Edward Snowden" -s /bin/bash edward

现在,让我们为用户edward设置密码:

root@ubuntu-linux:~# passwd edward 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

现在,关键时刻到了!让我们切换到用户edward,并列出他的主目录的内容:

root@ubuntu-linux:~# su - edward 
edward@ubuntu-linux:~$ ls 
welcome.txt
edward@ubuntu-linux:~$ cat welcome.txt 
Hello Friend!

你可以看到文件welcome.txt被复制到了edward的主目录。系统中创建的每个新用户现在都将有一个很酷的问候消息!请注意,像tomjerry这样的旧用户不会在他们的主目录中有文件welcome.txt,因为它们是在我们在/etc/skel中添加文件welcome.txt之前创建的。

更改默认值

我们已经厌倦了每次创建新用户时都要指定默认 shell。但幸运的是,有一个文件可以指定为任何新创建的用户设置默认 shell。这个神奇的文件是/etc/default/useradd

打开文件/etc/default/useradd,查找以下行:

SHELL=/bin/sh

将其更改为:

SHELL=/bin/bash

太棒了!现在,任何新创建的用户都将以/bin/bash作为默认 shell。让我们通过创建一个名为spy的新用户来测试一下:

root@ubuntu-linux:~# useradd -m spy

现在,为用户spy设置密码:

root@ubuntu-linux:~# passwd spy 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

最后,让我们切换到用户spy并检查默认 shell:

root@ubuntu-linux:~# su - spy 
spy@ubuntu-linux:~$ echo $SHELL
/bin/bash
spy@ubuntu-linux:~$ exit 
logout
root@ubuntu-linux:~#

万岁!我们可以看到bash是用户spy的默认 shell。

请记住,/bin/sh/bin/bash不是你系统上唯一两个有效的 shell;还有更多!查看文件/etc/shells,以查看系统上所有有效 shell 的完整列表:

root@ubuntu-linux:~# cat /etc/shells 
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash

你可以在/etc/default/useradd中更改其他用户默认值,包括:

  • 默认的home目录(HOME=/home

  • 默认的skel目录(SKEL=/etc/skel

我会把这个留给你作为练习。

删除用户

有时,不再需要用户在系统上,例如,离开公司的员工或只需要临时访问服务器的用户。无论哪种情况,您都需要知道如何删除用户。

我们创建的最后一个用户是spy,对吧?好吧,我们的系统上不需要间谍,所以让我们删除用户spy;您可以通过运行命令userdel spy来删除用户spy

root@ubuntu-linux:~# userdel spy

就像那样,用户spy被删除了。但是,spy的主目录仍然存在:

root@ubuntu-linux:~# ls -ld /home/spy
drwxr-xr-x 2 1008 1010 4096 Apr 17 10:24 /home/spy

我们将不得不手动删除它:

root@ubuntu-linux:~# rm -r /home/spy

但这很不方便。想象一下,每次删除一个用户后,您都必须手动删除他们的主目录。幸运的是,有一个更好的解决方案;您可以使用-r选项自动删除用户的主目录。

让我们尝试一下用户edward

root@ubuntu-linux:~# userdel -r edward

现在,让我们来检查一下用户edward的主目录是否仍然存在:

root@ubuntu-linux:~# ls -ld /home/edward
ls: cannot access '/home/edward': No such file or directory

正如您所看到的,edward的主目录已被删除。

/etc/group 文件

在学校,孩子们通常被分成不同的小组。例如,喜欢跳舞的孩子将成为舞蹈组的一部分。书呆子孩子将组成科学组。如果你想知道,我曾经是体育组的一部分,因为我跑得相当快!

在 Linux 中,具有相似特征的用户被放置在同一组中,这与我们有相同的概念。

什么是组?

组是共享相同角色或目的的用户集合。

所有组的信息都存储在文件/etc/group中。就像/etc/passwd文件一样,/etc/group中的每一行都对应于一个组,每一行都由4个字段组成。例如,Linux 中最著名的组之一是sudo组:

图 2:/etc/group 中的 4 个字段

以下表格详细说明了/etc/group中的这四个字段,并解释了每一个:

字段 它存储什么?
1 此字段存储组名。
2 此字段通常包含X,这意味着组密码已加密并存储在文件/etc/gshadow中。
3 此字段存储GID组 ID)号码。
4 此字段存储组成员的用户名。

表 11:理解/etc/group

添加组

让我们创建一个名为cartoon的组。为此,您需要运行命令groupadd cartoon

root@ubuntu-linux:~# groupadd cartoon

请注意,将添加包含组信息的新行到文件/etc/group的末尾:

root@ubuntu-linux:~# tail -n 1 /etc/group 
cartoon:x:1009:

请注意,组cartoon目前没有成员,这就是为什么第四个字段目前为空的原因。

让我们创建另一个名为developers的组,但这次,我们将指定888的 GID:

root@ubuntu-linux:~# groupadd --gid 888 developers

让我们检查/etc/group中的developers组条目:

root@ubuntu-linux:~# tail -n 1 /etc/group 
developers:x:888:

而且它看起来就像我们期望的那样。很酷!

添加组成员

用户tomjerry都是卡通人物,因此将它们都添加到cartoon组是有意义的。

要将tom添加到cartoon组,只需运行命令usermod -aG cartoon tom

root@ubuntu-linux:~# usermod -aG cartoon tom

同样,您可以将jerry添加到cartoon组中:

root@ubuntu-linux:~# usermod -aG cartoon jerry

现在,让我们来看看/etc/group文件:

root@ubuntu-linux:~# tail -n 2 /etc/group 
cartoon:x:1009:tom,jerry 
developers:x:888:

正如您所看到的,tomjerry现在都列为cartoon组的成员。

您可以使用id命令查看系统上任何用户的组成员资格。例如,如果您想要检查tom属于哪些组,可以运行命令id tom

root@ubuntu-linux:~# id tom
uid=444(tom) gid=1007(tom) groups=1007(tom),1009(cartoon)

让我们通过创建三个新用户sarapeterrachel来进行更多练习:

root@ubuntu-linux:~# useradd -m sara 
root@ubuntu-linux:~# useradd -m peter 
root@ubuntu-linux:~# useradd -m rachel

并记得为每个用户设置密码:

root@ubuntu-linux:~# passwd sara 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully 
root@ubuntu-linux:~# passwd peter 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully 
root@ubuntu-linux:~# passwd rachel 
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully 
root@ubuntu-linux:~#

现在想象一下,如果所有三个新用户都是软件开发人员;这意味着他们有相同的角色,因此他们应该是同一组的成员。因此,让我们将所有三个用户添加到developers组中:

root@ubuntu-linux:~# usermod -aG developers sara 
root@ubuntu-linux:~# usermod -aG developers peter 
root@ubuntu-linux:~# usermod -aG developers rachel

现在,让我们来看看/etc/group文件:

root@ubuntu-linux:~# tail -n 5 /etc/group 
cartoon:x:1009:tom,jerry 
developers:x:888:sara,peter,rachel 
sara:x:1001:
peter:x:1002: 
rachel:x:1003:

我们可以看到developers组现在有三个成员-sarapeterrachel。但是有一些奇怪的地方!看起来当我们创建用户sarapeterrachel时,它也创建了它们作为组!但是为什么会发生这种情况呢?好吧,让我在下一节中向您解释。

主要与次要组

Linux 中的每个用户必须是主要组的成员。主要组有时也被称为登录组。默认情况下,每当创建新用户时,也会创建一个与用户名称相同的组,并且该组将成为新用户的主要组。

另一方面,用户可能是或不是次要组的成员。次要组有时也被称为附加组。您可以将次要组视为用户除了用户的主要组之外的任何组的成员。

如果您还不理解主要和次要组的概念,不要担心;到本章结束时,它将变得非常清晰。

让我们创建一个名为dummy的新用户:

root@ubuntu-linux:~# useradd -m dummy

现在,如果您查看/etc/group文件的最后一行,您将看到一个名为dummy的组也被创建:

root@ubuntu-linux:~# tail -n 1 /etc/group 
dummy:x:1004:

这个dummy组是用户dummy的主要组;如果您对用户dummy运行id命令:

root@ubuntu-linux:~# id dummy
uid=1004(dummy) gid=1004(dummy) groups=1004(dummy)

您将看到用户dummy确实是dummy组的成员。现在,让我们将用户dummy添加到cartoon组:

root@ubuntu-linux:~# usermod -aG cartoon dummy

让我们再次对用户dummy运行id命令:

root@ubuntu-linux:~# id dummy
uid=1004(dummy) gid=1004(dummy) groups=1004(dummy),1009(cartoon)

您可以看到用户dummy是两个组dummycartoon的成员。但是,dummy是主要组,cartoon是次要组。

主要组始终在id命令的输出中以gid=开头:

图 3:主要与次要组

现在让我们将用户dummy添加到developers组:

root@ubuntu-linux:~# usermod -aG developers dummy

接下来,再次对用户dummy运行id命令:

root@ubuntu-linux:~# id dummy
uid=1004(dummy) gid=1004(dummy) groups=1004(dummy),1009(cartoon),888(developers)

如您所见,用户dummy是两个次要组cartoondevelopers的成员。

好了!够了这些虚拟的东西。让我们删除用户dummy

root@ubuntu-linux:~# userdel -r dummy

每个用户必须是唯一主要组的成员;但是,对主要组的选择没有限制!

为了演示,让我们创建一个名为smurf的用户,cartoon是用户smurf的主要组。这可以通过使用useradd命令的--gid选项轻松完成:

root@ubuntu-linux:~# useradd -m --gid cartoon smurf

现在,看一下/etc/group文件:

root@ubuntu-linux:~# tail -n 1 /etc/group 
rachel:x:1003:

您将看到没有使用名称smurf创建的组。太神奇了!那是因为我们已经为用户smurf指定了另一个主要组。

现在让我们检查用户smurf的组成员资格:

root@ubuntu-linux:~# id smurf
uid=1004(smurf) gid=1009(cartoon) groups=1009(cartoon)

如您所见,smurf只是cartoon组的成员,这当然也是他的主要组。

您还可以更改现有用户的主要组。例如,您可以将developers组设置为用户smurf的主要组,如下所示:

root@ubuntu-linux:~# usermod -g developers smurf 
root@ubuntu-linux:~# id smurf
uid=1004(smurf) gid=888(developers) groups=888(developers)

删除组

如果不再需要组,可以删除组。为了演示,让我们创建一个名为temp的组:

root@ubuntu-linux:~# groupadd temp

现在,您可以使用groupdel命令删除temp组:

root@ubuntu-linux:~# groupdel temp

现在,让我们尝试删除temp组:

root@ubuntu-linux:~# groupdel sara
groupdel: cannot remove the primary group of user 'sara'

我们收到错误消息,因为我们不允许删除现有用户的主要组。

文件所有权和权限

Linux 中的每个文件都由特定的用户和特定的组拥有。为了演示,让我们切换到用户smurf,并在smurf的主目录中创建一个名为mysmurf的文件:

root@ubuntu-linux:~# su - smurf 
smurf@ubuntu-linux:~$ touch mysmurf

现在对文件mysmurf进行长列表:

图 4:用户和组所有者

您将在输出的第三列中看到拥有文件的用户(用户所有者)的名称,默认情况下是创建文件的用户。

在输出的第四列中,您将看到文件的组(组所有者)的名称,默认情况下是用户所有者的主要组。

developers组是用户smurf的主要组,因此developers成为文件mysmurf的组所有者。

如果您在elliot的主目录中的sports目录上进行长列表:

smurf@ubuntu-linux:~$ ls -ld /home/elliot/sports
drwxr-xr-x 2 elliot elliot 4096 Oct 22 12:56 /home/elliot/sports

您将看到用户elliot是用户所有者,组elliot是组所有者;这是因为组elliot是用户elliot的主要组。

更改文件所有权

您可以使用chown命令更改文件的所有权。一般来说,chown命令的语法如下:

chown  user:group file

例如,您可以更改文件mysmurf的所有权,使用户elliot成为所有者,组cartoon成为组所有者,如下所示:

smurf@ubuntu-linux:~$
smurf@ubuntu-linux:~$ chown elliot:cartoon mysmurf
chown: changing ownership of 'mysmurf': Operation not permitted

哦!只有root用户可以做到;让我们切换到root用户并再试一次:

smurf@ubuntu-linux:~$ su - 
Password:
root@ubuntu-linux:~# cd /home/smurf
root@ubuntu-linux:/home/smurf# chown elliot:cartoon mysmurf

成功!现在让我们查看文件mysmurf的所有权:

root@ubuntu-linux:/home/smurf# ls -l mysmurf
-rw-r--r-- 1 elliot cartoon 0 Oct 22 15:09 mysmurf

如您所见,我们已成功更改了mysmurf的所有权。此外,您还可以更改用户所有者,而不更改组所有者。例如,如果您希望用户root成为mysmurf的所有者,可以运行以下命令:

root@ubuntu-linux:/home/smurf# chown root mysmurf 
root@ubuntu-linux:/home/smurf# ls -l mysmurf
-rw-r--r-- 1 root cartoon 0 Oct 22 15:09 mysmurf

如您所见,只有用户所有者更改为root,但cartoon仍然是组所有者。

您还可以更改组所有者,而不更改用户所有者。例如,如果您希望组developers成为mysmurf的组所有者,则可以运行:

root@ubuntu-linux:/home/smurf# chown :developers mysmurf 
root@ubuntu-linux:/home/smurf# ls -l mysmurf
-rw-r--r-- 1 root developers 0 Oct 22 15:09 mysmurf

供您参考

chgrp也可以用于更改文件的组所有者。我会留给你作为练习!

理解文件权限

在 Linux 中,每个文件都为三个不同的实体分配了访问权限;这些实体是:

  • 文件的用户所有者

  • 文件的组所有者

  • 其他所有人(也称为其他/全局)

我们已经熟悉了用户所有者和组所有者;其他所有人指的是系统上不是用户所有者也不是组所有者的任何用户。

您可以将这三个实体视为您、您的朋友和其他所有人。有一些事情你不愿意与任何人分享,其他一些事情你愿意与朋友分享,还有一些事情你可能愿意与所有人分享。

每个文件都有三种类型的访问权限:

  • 读取

  • 执行

每个这些访问权限的含义对文件和目录来说并不相同。以下图解释了文件与目录的访问权限之间的区别:

图 5:文件与目录权限

您可以通过进行长列表查看文件的权限。例如,要查看mysmurf文件上设置的当前权限,可以运行:

root@ubuntu-linux:~# ls -l /home/smurf/mysmurf
-rw-r--r-- 1 root developers 0 Oct 22 15:09 /home/smurf/mysmurf

现在注意输出的第一列,即-rw-r--r--。请注意,它由十个槽组成;第一个槽确定了文件的类型。剩下的九个槽分为三组,每组有三个槽,就像下图中的一样:

图 6:理解权限

注意第一个槽确定了文件类型;它可以是:

  • -表示常规文件

  • d表示目录

  • l表示软链接

  • b表示块设备

  • c表示字符设备

接下来的三个槽确定了文件所有者被授予的权限。这些槽中的第一个确定了读取权限;它可以是:

  • r表示读取权限

  • -表示无读取访问

这些槽中的第二个确定了写权限;它可以是:

  • w表示写访问

  • -表示无写访问

第三个槽确定了执行权限;它可以是:

  • x表示执行访问

  • -表示无执行访问

相同的逻辑也适用于接下来的三个槽,用于确定组所有者的权限,最后是最后三个槽,用于确定其他所有人的权限。

现在让我们动手做一些示例,以加强我们对文件权限的理解。让我们首先编辑mysmurf文件,并添加以下行Smurfs are blue!,使其看起来像这样:

root@ubuntu-linux:~# cat /home/smurf/mysmurf 
Smurfs are blue!

现在切换到用户smurf,并尝试读取文件mysmurf的内容:

root@ubuntu-linux:~# su - smurf 
smurf@ubuntu-linux:~$ cat mysmurf 
Smurfs are blue!

酷!用户smurf可以读取文件mysmurf的内容。请记住,用户smurf不是文件的所有者,但他是developers组的成员:

smurf@ubuntu-linux:~$ id smurf
uid=1004(smurf) gid=888(developers) groups=888(developers)

因此,smurf可以读取文件,因为mysmurf的组权限是r--。但是他能编辑文件吗?让我们看看如果用户smurf尝试向文件mysmurf添加一行我是 smurf!会发生什么:

smurf@ubuntu-linux:~$ echo "I am smurf!" >> mysmurf 
bash: mysmurf: Permission denied

权限被拒绝!是的,这是因为组所有者(或其他人)没有写权限。只有用户所有者对文件mysmurf有读和写权限,而在这种情况下所有者恰好是root。现在,如果我们改变文件所有权并使smurf成为文件mysmurf的所有者,那么他将能够编辑文件;所以让我们首先改变文件所有权:

smurf@ubuntu-linux:~$ su - 
Password:
root@ubuntu-linux:~# chown smurf /home/smurf/mysmurf 
root@ubuntu-linux:~# ls -l /home/smurf/mysmurf
-rw-r--r-- 1 smurf developers 17 Oct 23 11:06 /home/smurf/mysmurf

现在让我们切换回用户smurf,并尝试编辑文件mysmurf

root@ubuntu-linux:~# su - smurf
smurf@ubuntu-linux:~$ echo "I am smurf!" >> mysmurf 
smurf@ubuntu-linux:~$ cat mysmurf
Smurfs are blue!
I am smurf!

酷!所以用户smurf成功编辑了文件。现在让我们切换到用户elliot,并尝试向mysmurf文件添加一行我不是 smurf!

smurf@ubuntu-linux:~$ su - elliot 
Password:
elliot@ubuntu-linux:~$ cd /home/smurf/
elliot@ubuntu-linux:/home/smurf$ echo "I am not smurf!" >> mysmurf 
bash: mysmurf: Permission denied

权限被拒绝!请注意,elliot不是用户所有者,甚至不是developers组的成员,因此他被视为其他人。但是,他可以读取文件,因为其他人有读权限r--

elliot@ubuntu-linux:/home/smurf$ cat mysmurf 
Smurfs are blue!
I am smurf!

更改文件权限

现在,如果我们想要给elliot权限来编辑文件mysmurf,而不像之前那样改变文件所有权呢?好吧!这很简单;您可以使用chmod命令来更改文件权限。

让我们首先切换到root用户:

elliot@ubuntu-linux:/home/smurf$ su - 
Password:
root@ubuntu-linux:~# cd /home/smurf 
root@ubuntu-linux:/home/smurf#

现在您可以通过运行以下命令为其他人(其他所有人)添加写权限:

root@ubuntu-linux:/home/smurf# chmod o+w mysmurf

这里o+w表示其他人+写,这意味着向其他人添加写权限。现在对mysmurf进行长列表:

root@ubuntu-linux:/home/smurf# ls -l mysmurf
-rw-r--rw- 1 smurf developers 29 Oct 23 11:34 mysmurf

如您所见,其他人现在可以读取和写入mysmurf文件的rw-。现在,切换回用户elliot,并尝试再次添加一行我不是 smurf!

root@ubuntu-linux:/home/smurf# su elliot
elliot@ubuntu-linux:/home/smurf$ echo "I am not smurf!" >> mysmurf 
elliot@ubuntu-linux:/home/smurf$ cat mysmurf
Smurfs are blue!
I am smurf!
I am not smurf!

成功!用户elliot可以编辑文件mysmurf。现在是时候讨论执行权限了;让我们转到elliot的主目录,并创建一个名为mydate.sh的文件:

elliot@ubuntu-linux:/home/smurf$ cd /home/elliot 
elliot@ubuntu-linux:~$ touch mydate.sh

现在向文件mydate.sh添加以下两行:

#!/bin/bash 
date

您可以通过运行以下两个echo命令添加这两行:

elliot@ubuntu-linux:~$ echo '#!/bin/bash' >> mydate.sh 
elliot@ubuntu-linux:~$ echo date >> mydate.sh

现在不要担心#/bin/bash行的含义;我会在以后的章节中解释。无论如何,让我们查看文件mydate.sh的内容:

elliot@ubuntu-linux:~$ cat mydate.sh 
#!/bin/bash
date

现在对文件mydate.sh进行长列表:

elliot@ubuntu-linux:~$ ls -l mydate.sh
-rw-rw-r-- 1 elliot elliot 17 Oct 23 12:28 mydate.sh

请注意,这里每个人(用户所有者、组所有者和其他人)都没有执行权限。让我们为每个人添加执行权限;您可以通过运行以下命令来实现:

elliot@ubuntu-linux:~$ chmod a+x mydate.sh 
elliot@ubuntu-linux:~$ ls -l mydate.sh
-rwxrwxr-x 1 elliot elliot 17 Oct 23 12:28 mydate.sh

这里a+x表示所有+执行,这意味着向每个人添加执行权限。还要注意,我们之所以能够作为用户elliot运行chmod命令,是因为他是文件mydate.sh的所有者。

最后,只需输入mydate.sh的完整路径,然后按Enter

elliot@ubuntu-linux:~$ /home/elliot/mydate.sh 
Wed Oct 23 12:38:51 CST 2019

哇!当前日期显示出来了!您已经创建了您的第一个 Bash 脚本并运行了它!Bash 脚本将在以后的章节中详细介绍。但是现在至少您知道文件可执行是什么意思。现在通过运行以下命令删除执行权限:

elliot@ubuntu-linux:~$ chmod a-x mydate.sh 
elliot@ubuntu-linux:~$ ls -l mydate.sh
-rw-rw-r-- 1 elliot elliot 17 Oct 23 12:28 mydate.sh

这里a-x表示所有-执行,这意味着从每个人那里删除执行权限。现在尝试再次运行脚本:

elliot@ubuntu-linux:~$ /home/elliot/mydate.sh 
bash: /home/elliot/mydate.sh: Permission denied

我们收到了权限被拒绝的错误!这是因为文件mydate.sh不再可执行。大多数 Linux 命令都是可执行文件。例如,看一下date命令。首先,我们运行which命令以获取date命令的位置:

elliot@ubuntu-linux:~$ which date
/bin/date

现在对/bin/date进行长列表:

elliot@ubuntu-linux:~$ ls -l /bin/date
-rwxr-xr-x 1 root root 100568 Jan 18 2018 /bin/date

如您所见,每个人都有执行权限。现在看看当您删除执行权限时会发生什么:

elliot@ubuntu-linux:~$ su - 
Password:
root@ubuntu-linux:~# chmod a-x /bin/date

现在尝试运行date命令:

root@ubuntu-linux:~# date
-su: /bin/date: Permission denied

date命令不再起作用!请让我们通过重新添加执行权限来修复:

root@ubuntu-linux:~# chmod a+x /bin/date 
root@ubuntu-linux:~# date
Wed Oct 23 12:56:15 CST 2019

现在让我们删除文件mysmurf的用户所有者读取权限:

root@ubuntu-linux:~# cd /home/smurf/ 
root@ubuntu-linux:/home/smurf# chmod u-r mysmurf 
root@ubuntu-linux:/home/smurf# ls -l mysmurf
--w-r--rw- 1 smurf developers 45 Oct 23 12:02 mysmurf

这里的u-r表示用户读取,意思是从用户所有者中删除读取权限。现在让我们切换到用户smurf,并尝试读取文件mysmurf

root@ubuntu-linux:/home/smurf# su - smurf 
smurf@ubuntu-linux:~$ cat mysmurf
cat: mysmurf: Permission denied

可怜的smurf。他甚至不能读取自己的文件。但由于他是文件所有者,他可以恢复读取权限:

smurf@ubuntu-linux:~$ chmod u+r mysmurf 
smurf@ubuntu-linux:~$ cat mysmurf Smurfs are blue!
I am smurf!
I am not smurf!

您已经看到如何使用chmod命令添加(+)和删除(-)权限。您还可以使用等号=来设置权限。例如,如果您希望文件mysmurf的组所有者(developers)只有写入权限,您可以运行以下命令:

smurf@ubuntu-linux:~$ chmod g=w mysmurf 
smurf@ubuntu-linux:~$ ls -l mysmurf
-rw--w-rw- 1 smurf developers 45 Oct 23 12:02 mysmurf

所以现在,developers组成员只对文件mysmurf有写入权限-w-。以下是更多示例:

  • chmod ug=rwx mysmurf:这将给用户所有者和组所有者完全权限。

  • chmod o-rw mysmurf:这将从其他用户中删除读取和写入权限。

  • chmod a= mysmurf:这将为每个人提供零(无)权限。

  • chmod go= mysmurf:这将给组所有者和其他用户零权限。

  • chmod u+rx mysmurf:这将为用户所有者添加读取和执行权限。

让我们给每个人零权限:

smurf@ubuntu-linux:~$ chmod a= mysmurf 
smurf@ubuntu-linux:~$ ls -l mysmurf
---------- 1 smurf developers 45 Oct 23 12:02 mysmurf

所以现在用户smurf无法读取,写入或执行文件:

smurf@ubuntu-linux:~$ cat mysmurf 
cat: mysmurf: Permission denied
smurf@ubuntu-linux:~$ echo "Hello" >> mysmurf
-su: mysmurf: Permission denied

root用户呢?好吧,让我们切换到root来找出:

smurf@ubuntu-linux:~$ su - 
Password:
root@ubuntu-linux:~# cd /home/smurf/ 
root@ubuntu-linux:/home/smurf# cat mysmurf 
Smurfs are blue!
I am smurf!
I am not smurf!
root@ubuntu-linux:/home/smurf# echo "I got super powers" >> mysmurf 
root@ubuntu-linux:/home/smurf# cat mysmurf
Smurfs are blue!
I am smurf!
I am not smurf!
I got super powers
root@ubuntu-linux:/home/smurf# ls -l mysmurf
---------- 1 smurf developers 64 Oct 23 13:38 mysmurf

正如你所看到的,root用户可以做任何事情!这是因为root可以绕过文件权限!换句话说,文件权限不适用于root用户。

目录权限

现在让我们看看读取,写入和执行权限在目录上是如何工作的。最简单的例子将是root的主目录/root。让我们在/root上进行长列表:

root@ubuntu-linux:~# ls -ld /root
drwx------ 5 root root 4096 Oct 22 14:28 /root

正如您所看到的,root所有者被授予完全权限,其他人被授予零权限。让我们在/root内创建一个名为gold的文件:

root@ubuntu-linux:~# touch /root/gold

现在让我们切换到用户smurf,并尝试列出/root目录的内容:

root@ubuntu-linux:~# su - smurf 
smurf@ubuntu-linux:~$ ls /root
ls: cannot open directory '/root': Permission denied

用户smurf收到了权限被拒绝的错误,因为他在目录/root上没有读取权限。现在,smurf能在/root内创建文件吗?

smurf@ubuntu-linux:~$ touch /root/silver
touch: cannot touch '/root/silver': Permission denied

他不能,因为他在/root上没有写入权限。他能删除/root内的文件吗?

smurf@ubuntu-linux:~$ rm /root/gold
rm: cannot remove '/root/gold': Permission denied

同样,没有写入权限,所以他无法在/root中删除文件。最后,用户smurf能否切换到/root目录?

smurf@ubuntu-linux:~$ cd /root
-su: cd: /root: Permission denied

他不能,因为smurf需要执行权限才能切换到/root目录。现在,让我们切换回root用户并开始添加一些权限:

smurf@ubuntu-linux:~$ exit 
logout
root@ubuntu-linux:~# chmod o+rx /root

在这里,我们为其他用户添加了读取和执行权限,所以用户smurf现在可以列出/root目录的内容:

root@ubuntu-linux:~# su - smurf 
smurf@ubuntu-linux:~$ ls /root 
gold

他甚至可以切换到/root目录,因为我们还添加了执行权限:

smurf@ubuntu-linux:~$ cd /root 
smurf@ubuntu-linux:/root$

但他仍然没有写入权限,所以他无法在/root中创建或删除文件:

smurf@ubuntu-linux:/root$ rm gold
rm: remove write-protected regular empty file 'gold'? y 
rm: cannot remove 'gold': Permission denied 
smurf@ubuntu-linux:/root$ touch silver
touch: cannot touch 'silver': Permission denied

让我们为其他用户添加写入权限:

smurf@ubuntu-linux:/root$ su - 
Password:
root@ubuntu-linux:~# chmod o+w /root

最后,切换到用户smurf并尝试在/root中创建或删除文件:

smurf@ubuntu-linux:~$ cd /root 
smurf@ubuntu-linux:/root$ rm gold
rm: remove write-protected regular empty file 'gold'? y 
smurf@ubuntu-linux:/root$ touch silver
smurf@ubuntu-linux:/root$ ls 
silver

所以smurf现在可以在/root中创建和删除文件,因为他有写入权限。

使用八进制表示法

您可以使用数字421来设置文件权限,而不是字母rwx。看一下下面的图片:

图 7:理解八进制表示法

请注意,第一个数字7基本上是三个数字的相加:4(r)+ 2(w)+ 1(x),这将为文件所有者设置完全权限。第二个数字6是两个数字的相加:4(r)+ 2(w),这将为组所有者设置读取和写入权限。最后,第三个数字4,这将为其他用户设置读取权限。

我知道你在想什么:“为什么我要做数学,当我可以使用文字表示rwx?”相信我,我理解你。很多人更喜欢文字表示法而不是数字表示法,但有些人太喜欢数字了!

让我们用八进制表示法做一些练习。文件mysmurf当前没有任何权限:

smurf@ubuntu-linux:~$ ls -l mysmurf
---------- 1 smurf developers 64 Oct 23 13:38 mysmurf

我们可以使用777为每个人提供完全权限:

smurf@ubuntu-linux:~$ chmod 777 mysmurf 
smurf@ubuntu-linux:~$ ls -l mysmurf
-rwxrwxrwx 1 smurf developers 64 Oct 23 13:38 mysmurf

太棒了!现在你可以使用三位数421来给予文件所有者读取权限,给予组所有者写入权限,以及给予其他用户执行权限:

smurf@ubuntu-linux:~$ chmod 421 mysmurf 
smurf@ubuntu-linux:~$ ls -l mysmurf
-r---w---x 1 smurf developers 64 Oct 23 13:38 mysmurf

让我们再举一个例子。如果你想给予文件所有者完全权限,给予组所有者读取权限,以及其他用户零权限,那很简单;正确的三位数将是740

smurf@ubuntu-linux:~$ chmod 740 mysmurf 
smurf@ubuntu-linux:~$ ls -l mysmurf
-rwxr----- 1 smurf developers 64 Oct 23 13:38 mysmurf

一旦你掌握了,数字就很容易使用。只需要记住:

  • 4:读取

  • 2:写入

  • 1:执行

  • 0:零权限

以下表总结了所有可能的权限组合:

数字 意义 字面等价
0 零/无权限 ---
1 执行 --x
2 写入 -w-
3 写入 + 执行 -wx
4 读取 r--
5 读取 + 执行 r-x
6 读取 + 写入 rw-
7 读取 + 写入 + 执行 rwx

表 12:八进制表示法与字面表示法

这一章有点冗长。休息一下,然后回来完成知识检测练习!

知识检测

对于以下练习,打开你的终端并尝试解决以下任务:

  1. 创建一个用户abraham,用户 ID 为333

  2. 创建一个新的组admins

  3. 将用户abraham添加到admins组。

  4. admins设为目录/home/abraham的组所有者。

  5. admins组的成员只能列出目录/home/abraham的内容。

真或假

  1. chmod a=rxw facts.txt将会得到与chmod 777 facts.txt相同的结果。

  2. chmod a=rw facts.txt将会得到与chmod 665 facts.txt相同的结果。

  3. 用户elliot可以有多个主要组。

管道和 I/O 重定向

Linux 的一个主要原则是每个程序都做一件事情,因此,每个 Linux 命令都设计成能够高效地完成单个任务。在本章中,你将学习如何使用 Linux 管道来结合命令的功能,以执行更复杂的任务。你还将学习有关 I/O(输入/输出)重定向,这将使你能够读取用户输入并将命令输出保存到文件中。

第十章:Linux 管道

在 Linux 中,你可以使用管道将一个命令的输出发送到另一个命令的输入(参数)中:

图 1- Linux 管道

在你的键盘上,管道由竖线字符表示。Linux 管道非常有用,因为它们允许你以一种简单的方式完成相对复杂的任务,而且在整本书中,你会发现它们经常派上用场。

在我们做一个例子之前,让我们先将hard.txt文件重命名为facts.txt,因为我们在第六章中删除了facts.txt文件,硬链接与软链接

elliot@ubuntu-linux:~$ mv hard.txt facts.txt

现在让我们使用head命令来查看facts.txt的前五行:

elliot@ubuntu-linux:~$ head -n 5 facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.

现在我想显示文件facts.txt的第五行Sky is high.;我该怎么做呢?

这就是 Linux 管道的威力所在。如果你将前一个命令的输出传输到tail -n 1命令,你将得到第五行:

elliot@ubuntu-linux:~$ head -n 5 facts.txt | tail -n 1 
Sky is high.

因此,通过使用管道,我能够将head -n 5 facts.txt命令的输出发送到tail -n 1命令的输入(参数)中。

让我们做另一个例子。如果你想显示文件facts.txt的第七行,那么你将使用head命令显示前七行,然后使用管道tail最后一行:

elliot@ubuntu-linux:~$ head -n 7 facts.txt | tail -n 1 
Linux is awesome

你也可以同时使用多个管道,就像下面的图表中演示的那样:

图 2:两个管道

例如,你已经知道lscpu命令会显示处理器信息。lscpu命令的第四行输出显示了你的机器有多少个 CPU。你可以使用两个管道显示lscpu命令的第四行:

elliot@ubuntu-linux:~$ lscpu | head -n 4 | tail -n 1 
CPU(s):       1

所以让我们分解一下这里发生了什么。我们使用的第一个管道是显示lscpu命令的前四行:

elliot@ubuntu-linux:~$ lscpu | head -n 4 
Architecture:    x86_64
CPU op-mode(s):  32-bit, 64-bit 
Byte Order:      Little Endian
CPU(s):          1

然后我们使用第二个管道来tail最后一行,这样就得到了第四行:

elliot@ubuntu-linux:~$ lscpu | head -n 4 | tail -n 1 
CPU(s):        1

你可以类似地显示lscpu的第二行,其中显示了 CPU 的操作模式,但我会把这个留给你作为一个练习。

输入和输出重定向

在本节中,你将学习 Linux 最酷的功能之一,即 I/O(输入/输出)重定向。大多数 Linux 命令都使用三种不同的数据流:

  • 标准输入(也称为stdin

  • 标准输出(也称为stdout

  • 标准错误(也称为stderr

到目前为止,我们讨论的大多数命令都会产生一些输出。这些输出被发送到一个称为标准输出(也称为stdout)的特殊文件中。默认情况下,标准输出文件链接到终端,这就是为什么每次运行命令时,你都会在终端上看到输出。有时候命令会产生错误消息。这些错误消息被发送到另一个称为标准错误(也称为stderr)的特殊文件中,它也默认链接到终端。

重定向标准输出

你知道运行date命令会在你的终端上显示当前日期:

elliot@ubuntu-linux:~$ date 
Sat May 11 06:02:44 CST 2019

现在通过使用大于号>,你可以将date命令的输出重定向到文件而不是你的终端!看一下:

elliot@ubuntu-linux:~$ date > mydate.txt

如你所见,屏幕上没有显示任何输出!这是因为输出被重定向到文件mydate.txt

elliot@ubuntu-linux:~$ cat mydate.txt 
Sat May 11 06:04:49 CST 2019

太棒了!让我们再试一些例子。你可以使用echo命令在你的终端上打印一行:

elliot@ubuntu-linux:~$ echo "Mars is a planet." 
Mars is a planet.

如果您想将输出重定向到一个名为planets.txt的文件,您可以运行以下命令:

elliot@ubuntu-linux:~$ echo "Mars is a planet." > planets.txt 
elliot@ubuntu-linux:~$ cat planets.txt
Mars is a planet

太棒了!请注意,文件planets.txt也在这个过程中创建了。现在让我们向文件planets.txt添加更多的行星:

elliot@ubuntu-linux:~$ echo "Saturn is a planet." > planets.txt 
elliot@ubuntu-linux:~$ cat planets.txt
Saturn is a planet.

嗯。我们添加了一行“土星是一个行星。”但现在删除了“火星是一个行星。”!这是因为用>重定向标准输出会覆盖文件。在这种情况下,我们需要的是向文件追加,这可以通过使用双大于号>>来实现。所以现在让我们向文件planets.txt追加一行“火星是一个行星。”:

elliot@ubuntu-linux:~$ echo "Mars is a planet." >> planets.txt 
elliot@ubuntu-linux:~$ cat planets.txt
Saturn is a planet.
Mars is a planet.

太棒了!正如你所看到的,它将“火星是一个行星。”添加到文件的末尾。让我们再添加一个行星:

elliot@ubuntu-linux:~$ echo "Venus is a planet." >> planets.txt 
elliot@ubuntu-linux:~$ cat planets.txt
Saturn is a planet.
Mars is a planet.
Venus is a planet.

太棒了!这里还有一件事情你需要知道,那就是标准输出(stdout)链接到文件描述符 1。

文件描述符是什么?

文件描述符是一个在计算机操作系统中唯一标识打开文件的数字。

然后运行命令:

elliot@ubuntu-linux:~$ date > mydate.txt

与运行命令相同:

elliot@ubuntu-linux:~$ date 1> mydate.txt

请注意,1>中的1是指文件描述符 1(stdout)。

重定向标准错误

如果您尝试显示一个不存在的文件的内容,您将收到一个错误消息:

elliot@ubuntu-linux:~$ cat blabla 
cat: blabla: No such file or directory

现在,这个错误消息来自标准错误(stderr)。如果您尝试以与标准输出相同的方式重定向错误,它将不起作用:

elliot@ubuntu-linux:~$ cat blabla > error.txt 
cat: blabla: No such file or directory

正如您所看到的,它仍然在您的终端上显示错误消息。这是因为stderr链接到文件描述符 2。因此,要重定向错误,您必须使用2>

elliot@ubuntu-linux:~$ cat blabla 2> error.txt

现在如果您显示文件error.txt的内容,您将看到错误消息:

elliot@ubuntu-linux:~$ cat error.txt 
cat: blabla: No such file or directory

让我们尝试删除一个不存在的文件:

elliot@ubuntu-linux:~$ rm brrrr
rm: cannot remove 'brrrr': No such file or directory

这也会产生一个错误消息。我们可以将这个错误消息追加到文件中

使用2>>error.txt

elliot@ubuntu-linux:~$ rm brrrr 2>> error.txt

现在如果您显示文件error.txt的内容:

elliot@ubuntu-linux:~$ cat error.txt 
cat: blabla: No such file or directory
rm: cannot remove 'brrrr': No such file or directory

您将看到两个错误消息。

将所有输出重定向到同一个文件

有些情况下,您可能同时获得标准输出和错误消息。例如,如果您运行以下命令:

elliot@ubuntu-linux:~$ cat planets.txt blabla 
Saturn is a planet.
Mars is a planet.
Venus is a planet.
cat: blabla: No such file or directory

您会看到它显示了文件planets.txt的内容,但在最后一行也显示了一个错误消息(因为没有文件blabla来连接)。

您可以选择将错误重定向到另一个文件:

elliot@ubuntu-linux:~$ cat planets.txt blabla 2> err.txt 
Saturn is a planet.
Mars is a planet.
Venus is a planet.

这样,您只能在屏幕上看到标准输出。或者您可以选择重定向标准输出:

elliot@ubuntu-linux:~$ cat planets.txt blabla 1> output.txt 
cat: blabla: No such file or directory

这样,您只能在屏幕上看到错误。那么,如果您想将标准输出和错误重定向到同一个文件呢?在这种情况下,您必须运行:

elliot@ubuntu-linux:~$ cat planets.txt blabla > all.txt 2>&1

&1是指标准输出,而2>是指标准错误。所以我们基本上是在说:“将 stderr 重定向到我们正在重定向 stdout 的地方。”

现在如果您显示文件all.txt的内容:

elliot@ubuntu-linux:~$ cat all.txt 
Saturn is a planet.
Mars is a planet.
Venus is a planet.
cat: blabla: No such file or directory

您可以看到它包括stdoutstderr

丢弃输出

有时候你不需要将输出重定向到任何地方;你只是想抛弃它并摆脱它。在这种情况下,你可以将输出重定向到/dev/null。这通常与错误消息一起使用。例如:

elliot@ubuntu-linux:~$ cat planets.txt blabla 2> /dev/null 
Saturn is a planet.
Mars is a planet.
Venus is a planet.

这将把错误消息重定向到/dev/null。您可以将/dev/null视为垃圾收集器。

重定向标准输入

一些 Linux 命令通过标准输入与用户输入交互(默认情况下是键盘)。例如,read命令从用户那里读取输入并将其存储在一个变量中。例如,您可以运行命令read weather

elliot@ubuntu-linux:~$ read weather 
It is raining.

然后它将等待您输入一行文本。我输入了一行“下雨了。”,所以它将这行存储在weather变量中。您可以使用echo命令来显示变量的内容:

elliot@ubuntu-linux:~$ echo $weather 
It is raining.

请注意,您必须在变量名之前加上美元符号。read命令在 shell 脚本中特别有用,我们稍后会涉及到。现在请注意,我使用键盘写下了It is raining.这一行。然而,我可以使用小于号<将标准输入重定向到文件中,例如:

elliot@ubuntu-linux:~$ read message < mydate.txt

这将读取文件mydate.txt的内容并将其存储在message变量中:

elliot@ubuntu-linux:~$ echo $message 
Sat May 11 06:34:52 CST 2019

正如您所看到的,变量message现在具有与文件my-date.txt相同的内容。

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 仅显示文件facts.txt的第 5 行。

  2. free命令的输出保存到名为system.txt的文件中。

  3. lscpu命令的输出追加到文件system.txt中。

  4. 运行命令rmdir /var并将错误消息重定向到文件error.txt

分析和操作文件

在本章中,你将学习各种 Linux 命令,这些命令将帮助你分析和操作文件。你还将学习如何比较两个文件并获取文件大小。你还将学习如何显示文件的类型,并显示文件中的字符数、单词数和行数。此外,你还将学习如何对文件进行排序、删除重复行等等!

第十一章:找出不同之处

你可以使用diff命令比较两个文件的内容,并突出它们之间的差异。

为了演示,让我们首先复制文件facts.txt并命名为facts2.txt

elliot@ubuntu-linux:~$ cp facts.txt facts2.txt

现在让我们将行"Brazil is a country."附加到文件facts2.txt中:

elliot@ubuntu-linux:~$ echo "Brazil is a country." >> facts2.txt

现在,在两个文件上运行diff命令:

elliot@ubuntu-linux:~$ diff facts.txt facts2.txt 
12a13
> Brazil is a country.

酷!它输出了两个文件之间的差异,这种情况下是行Brazil is a country.

查看文件大小

你可以使用du命令查看文件大小。du代表磁盘使用。如果你想查看文件中有多少字节,你可以使用du命令和-b选项:

elliot@ubuntu-linux:~$ du -b facts.txt
210 facts.txt

facts.txt文件有210字节。一个字符等于一个字节的大小,所以现在你知道facts.txt文件确切地有210个字符。

你还可以使用-h选项,它将以人类可读的格式打印文件大小。例如,要查看dir1目录及其内容的大小,你可以运行:

elliot@ubuntu-linux:~$ du -h dir1 
4.0K     dir1/cities
16K     dir1/directory2 
24K     dir1

计算字符、单词和行数

单词计数wc命令是另一个非常方便的命令。它计算文件中的行数、单词数和字符数。例如,要显示文件facts.txt中的行数,你可以使用-l选项:

elliot@ubuntu-linux:~$ wc -l facts.txt
12 facts.txt

文件facts.txt中总共有12行。要显示单词的数量,你可以使用-w选项:

elliot@ubuntu-linux:~$ wc -w facts.txt
37 facts.txt

所以文件facts.txt中总共有37个单词。要显示字符(字节)的数量,你可以使用-c选项:

elliot@ubuntu-linux:~$ wc -c facts.txt
210 facts.txt

文件facts.txt中总共有210个字符。没有任何选项,wc命令将以并列的方式显示行数、单词数和字符数:

elliot@ubuntu-linux:~$ wc facts.txt
12 37 210 facts.txt

查看文件类型

你可以使用file命令来确定文件的类型。例如,如果你想确定文件/var的类型,你可以运行:

elliot@ubuntu-linux:~$ file /var
/var: directory

正如你所期望的那样,输出显示/var是一个目录。如果你想显示facts.txt文件的类型,你可以运行:

elliot@ubuntu-linux:~$ file facts.txt 
facts.txt: ASCII text

输出显示facts.txt是一个 ASCII 文本文件。

ASCII 是什么?

ASCII,即美国信息交换标准代码,是用数字表示128个英文字符的代码,每个字母被分配一个从0127的数字。

你的计算机不理解人类语言(字母),只理解数字!因此,英语语言中的每个字符都被转换为一个数字。你的计算机将任何文本文件都视为一堆堆的数字!

现在让我们创建一个名为soft.txt的软链接到facts.txt文件:

elliot@ubuntu-linux:~$ ln -s soft.txt facts.txt

并在soft.txt上运行file命令:

elliot@ubuntu-linux:~$ file soft.txt 
soft.txt: symbolic link to facts.txt

如你所见,它显示soft.txt是一个指向facts.txt的符号(软)链接。

文件排序

你可以使用sort命令对文本文件进行排序。例如,你可以通过运行命令按字母顺序查看facts.txt文件:

elliot@ubuntu-linux:~$ sort facts.txt 
Apples are red.
Bananas are yellow.
Cherries are red.
Cherries are red.
Cherries are red.
Cherries are red.
Earth is round.
Grapes are green.
Grass is green.
Linux is awesome!
Sky is high.
Swimming is a sport.

你还可以使用-r选项以相反的顺序进行排序:

elliot@ubuntu-linux:~$ sort -r facts.txt 
Swimming is a sport.
Sky is high.
Linux is awesome!
Grass is green.
Grapes are green.
Earth is round.
Cherries are red.
Cherries are red.
Cherries are red.
Cherries are red.
Bananas are yellow.
Apples are red.

你也可以使用-n选项按数字值而不是文字值进行排序。

显示唯一行

你可以使用uniq命令省略文件中重复的行。例如,注意文件facts.txt中的行Cherries are red.被包含了四次:

要查看facts.txt而不重复的行,你可以运行:

elliot@ubuntu-linux:~$ uniq facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Sky is high.
Earth is round.
Linux is awesome!
Cherries are red.
Grass is green.
Swimming is a sport.

注意Cherries are red.在输出中仍然显示了两次。这是因为uniq命令只省略了重复的行而不是重复的行!如果你想省略重复的行,你必须首先对文件进行sort,然后使用管道在排序输出上应用uniq命令:

elliot@ubuntu-linux:~$ sort facts.txt | uniq 
Apples are red.
Bananas are yellow.
Cherries are red.
Earth is round.
Grapes are green.
Grass is green.
Linux is awesome!
Sky is high.
Swimming is a sport.

哇!我们成功地省略了重复和重复的行。

搜索模式

grep命令是 Linux 中最受欢迎和有用的命令之一。您可以使用grep打印与特定模式匹配的文本行。例如,如果您只想显示facts.txt中包含单词green的行,则可以运行:

elliot@ubuntu-linux:~$ grep green facts.txt 
Grapes are green.
Grass is green.

如您所见,它只打印了包含单词green的两行。

grep命令在与管道一起使用时也可能非常有用。例如,要仅列出您的主目录中的txt文件,可以运行以下命令:

elliot@ubuntu-linux:~$ ls | grep txt 
all.txt
error.txt 
facts2.txt 
facts.txt 
Mars.txt 
mydate.txt 
output.txt 
planets.txt 
soft.txt

您可以使用-i选项使您的搜索不区分大小写。例如,如果您想要打印包含单词Earth的行在facts.txt中,然后使用以下命令:

elliot@ubuntu-linux:~$ grep earth facts.txt 
elliot@ubuntu-linux:~$

这将不显示任何结果,因为grep默认区分大小写。但是,如果您传递-i选项:

elliot@ubuntu-linux:~$ grep -i earth facts.txt 
Earth is round.

它将使搜索不区分大小写,因此它将显示行Earth is round.

流编辑器

您可以使用流编辑器命令sed来过滤和转换文本。例如,要在facts.txt中用单词Cloud替换单词Sky,可以运行以下命令:

elliot@ubuntu-linux:~$ sed 's/Sky/Cloud/' facts.txt 
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Cloud is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.
Swimming is a sport.

如您在输出中所见,单词Sky被替换为Cloud。但是,文件facts.txt没有被编辑。要覆盖(编辑)文件,可以使用-i选项:

elliot@ubuntu-linux:~$ sed -i 's/Sky/Cloud/' facts.txt 
elliot@ubuntu-linux:~$ cat facts.txt
Apples are red.
Grapes are green.
Bananas are yellow.
Cherries are red.
Cloud is high.
Earth is round.
Linux is awesome!
Cherries are red.
Cherries are red.
Cherries are red.
Grass is green.
Swimming is a sport.

如您所见,更改已反映在文件中。

翻译字符

您可以使用tr命令来翻译字符。我这里不是在谈论将文本翻译成不同的语言;相反,我是在使用“翻译”一词的第二个含义,即从一种形式转换为另一种形式。

如果您阅读tr命令的man页面,您会在描述中看到:从标准输入翻译、压缩和/或删除字符,写入标准输出。因此,tr命令不接受任何参数。

tr命令的一个常见用途是将小写字母转换为大写字母(反之亦然)。例如,如果您想要以大写形式显示facts.txt中的所有单词,可以运行:

elliot@ubuntu-linux:~$ cat facts.txt | tr [:lower:] [:upper:] 
APPLES ARE RED.
GRAPES ARE GREEN.
BANANAS ARE YELLOW.
CHERRIES ARE RED.
CLOUD IS HIGH.
EARTH IS ROUND.
LINUX IS AWESOME!
CHERRIES ARE RED.
CHERRIES ARE RED.
CHERRIES ARE RED.
GRASS IS GREEN.
SWIMMING IS A SPORT.

您还可以显示所有单词的小写形式:

elliot@ubuntu-linux:~$ cat facts.txt | tr [:upper:] [:lower:] 
apples are red.
grapes are green. 
bananas are yellow. 
cherries are red. 
cloud is high. 
earth is round. 
linux is awesome! 
cherries are red. 
cherries are red. 
cherries are red. 
grass is green. 
swimming is a sport.

你也可以使用-d选项来删除字符。例如,要删除facts.txt中的所有空格,可以运行:

elliot@ubuntu-linux:~$ cat facts.txt | tr -d ' ' 
Applesarered.
Grapesaregreen.
Bananasareyellow.
Cherriesarered.
Cloudishigh.
Earthisround.
Linuxisawesome!
Cherriesarered.
Cherriesarered.
Cherriesarered.
Grassisgreen.
Swimmingisasport.

一个很酷的提示

tr命令不会更改(编辑)文件的内容。它只会将更改写入标准输出。但是,您可以使用输出重定向将输出存储到另一个文件中。

例如,运行以下命令:

elliot@ubuntu-linux:~$ cat facts.txt | tr [:lower:] [:upper:] > upper.txt

将命令的输出存储到:

cat facts.txt | tr [:lower:] [:upper:]

文件upper.txt中。

切割文本

如果您只想查看文件的一部分(或一节),那么cut命令可能非常有用。例如,您可以看到facts.txt文件中的每行都由单个空格分隔的多个单词组成。如果您只想查看每行的第一个单词(第一列/字段),那么可以运行以下命令:

elliot@ubuntu-linux:~$ cut -d ' ' -f1 facts.txt 
Apples
Grapes 
Bananas 
Cherries 
Cloud 
Earth 
Linux 
Cherries 
Cherries 
Cherries 
Grass 
Swimming

-d选项是分隔符,必须是单个字符。在这种情况下,我选择了空格字符' '作为分隔符。我还使用了-f1选项来仅查看第一个字段(列)。

如果您想查看每行的第三个单词(第三个字段),则可以使用-f3而不是-f1,如下所示:

elliot@ubuntu-linux:~$ cut -d ' ' -f3 facts.txt 
red.
green. 
yellow. 
red. 
high. 
round. 
awesome! 
red. 
red. 
red. 
green. 
a

您还可以一次选择多个字段。例如,要查看每行的第一个和第三个单词,可以使用-f1,3

elliot@ubuntu-linux:~$ cut -d ' ' -f1,3 facts.txt 
Apples red.
Grapes green.
Bananas yellow.
Cherries red.
Cloud high.
Earth round.
Linux awesome!
Cherries red.
Cherries red.
Cherries red.
Grass green.
Swimming a 

使用 awk 进行文本处理

awk是一个非常强大的工具,您可以在 Linux 中用来分析和处理文本。实际上,awk不像您迄今为止学到的任何命令,这是因为awk实际上是一种编程语言。您会发现有些书专门写来解释和讨论awk的用法。但是,我只会在这里向您展示awk的基础知识,您可以自己深入研究。

您可以使用awk来实现与cut命令相同的功能。例如,要查看文件facts.txt中每行的第一个单词,可以运行:

elliot@ubuntu-linux:~$ awk '{print $1}' facts.txt 
Apples
Grapes 
Bananas 
Cherries 
Cloud 
Earth 
Linux 
Cherries 
Cherries 
Cherries 
Grass 
Swimming

请注意,我们不需要指定空格字符' '作为分隔符,就像我们在cut命令中所做的那样,这是因为awk足够聪明,可以自己弄清楚。您还可以一次查看多个字段;例如,要查看每行的第一个和第二个单词,您可以运行:

elliot@ubuntu-linux:~$ awk '{print $1,$2}' facts.txt 
Apples are
Grapes are 
Bananas are 
Cherries are 
Cloud is 
Earth is 
Linux is 
Cherries are 
Cherries are 
Cherries are 
Grass is 
Swimming is

awkcut有一个优势,那就是awk足够聪明,即使每个字段之间有多个字符分隔,也能将文件分隔成不同的字段。cut命令只有在文件有单个分隔符时才有效,比如单个空格、冒号、逗号等。

为了演示,创建一个名为animals.txt的文件,并插入以下四行:

fox        is smart
whale is   big
cheetah  is           fast 
penguin     is cute

不要编辑格式;保持空格混乱:

elliot@ubuntu-linux:~$ cat animals.txt 
fox        is smart
whale is   big
cheetah  is           fast 
penguin     is cute

现在,如果您尝试使用cut命令仅显示每行中的第三个单词,它将失败,因为每个单词之间有多个空格分隔。

然而,awk足够聪明,可以弄清楚:

elliot@ubuntu-linux:~$ awk '{print $3}' animals.txt 
smart
big 
fast 
cute

正如您所看到的,每行中的第三个单词被显示出来。您也可以使用awk来搜索模式,就像grep命令一样。例如,要打印包含facts.txt中单词red的行,您可以运行以下命令:

elliot@ubuntu-linux:~$ awk '/red/{print}' facts.txt 
Apples are red.
Cherries are red. 
Cherries are red. 
Cherries are red. 
Cherries are red. 

通配符字符

通配符字符是 Linux 中的特殊字符,它们用于指定一组(类)字符。表 13列出了所有 Linux 通配符:

通配符 它的作用
* 匹配任何字符。
? 匹配任何单个字符。
[characters] 匹配属于字符集的字符。例如,[abc]将匹配字符abc
[!characters] 匹配不属于字符集的任何字符。基本上是[characters]的否定。例如,[!abc]将匹配任何不是abc的字符。
[[:class:]] 匹配属于字符类的任何字符。

表 13:Linux 通配符

在我们讨论tr命令时,您已经看到了字符类。记住[:lower:][:upper:]代表小写和大写字母,这是字符类的两个示例。表 14列出了最常见的字符类:

字符类 它代表什么
[:alnum:] 代表所有的字母数字,即任何字母或数字。
[:alpha:] 代表所有的字母,即任何字母。
[:digit:] 代表所有的数字,即任何数字。
[:lower:] 代表任何小写字母。
[:upper:] 代表任何大写字母。

表 14:字符类

好了,够了这些理论!让我们看一些例子。您可以使用*通配符来列出您的主目录中的所有txt文件:

elliot@ubuntu-linux:~$ ls -l *.txt
-rw-rw-r-- 1 elliot elliot  96 May 11 07:01 all.txt
-rw-rw-r-- 1 elliot elliot  91 May 12 06:10 animals.txt
-rw-rw-r-- 1 elliot elliot  92 May 11 06:48 error.txt
-rw-rw-r-- 1 elliot elliot 231 May 11 08:28 facts2.txt
-rw-rw-r-- 1 elliot elliot 212 May 11 18:37 facts.txt
-rw-rw-r-- 1 elliot elliot  18 May 11 06:12 Mars.txt
-rw-rw-r-- 1 elliot elliot  29 May 11 06:34 mydate.txt
-rw-rw-r-- 1 elliot elliot  57 May 11 07:00 output.txt
-rw-rw-r-- 1 elliot elliot  57 May 11 06:20 planets.txt
lrwxrwxrwx 1 elliot elliot  9  May  8 22:02 soft.txt -> facts.txt
-rw-rw-r-- 1 elliot elliot 212 May 12 05:09 upper.txt

如果您只想列出以字母f开头的文件名,您可以使用f*

elliot@ubuntu-linux:~$ ls -l f*
-rw-rw-r-- 1 elliot elliot 231 May 11 08:28 facts2.txt
-rw-rw-r-- 1 elliot elliot 212 May 11 18:37 facts.txt

如果您想列出包含三个字母后跟.txt扩展名的文件名,那么您可以使用?通配符:

elliot@ubuntu-linux:~$ ls -l ???.txt
-rw-rw-r-- 1 elliot elliot 96 May 11 07:01 all.txt

您还可以同时使用多个通配符。例如,如果您只想列出以字母af开头的文件名,您可以使用[af]通配符,后跟*通配符:

elliot@ubuntu-linux:~$ ls -l [af]*
-rw-rw-r-- 1 elliot elliot 96 May 11 07:01 all.txt
-rw-rw-r-- 1 elliot elliot 91 May 12 06:10 animals.txt
-rw-rw-r-- 1 elliot elliot 231 May 11 08:28 facts2.txt
-rw-rw-r-- 1 elliot elliot 212 May 11 18:37 facts.txt

您还可以使用集合否定,例如,要列出所有以除f之外的任何字母开头的.txt文件名,您可以运行[!f]*

elliot@ubuntu-linux:~$ ls -l [!f]*.txt
-rw-rw-r-- 1 elliot elliot 96 May 11 07:01 all.txt
-rw-rw-r-- 1 elliot elliot 91 May 12 06:10 animals.txt
-rw-rw-r-- 1 elliot elliot 92 May 11 06:48 error.txt
-rw-rw-r-- 1 elliot elliot 18 May 11 06:12 Mars.txt
-rw-rw-r-- 1 elliot elliot 29 May 11 06:34 mydate.txt
-rw-rw-r-- 1 elliot elliot 57 May 11 07:00 output.txt
-rw-rw-r-- 1 elliot elliot 57 May 11 06:20 planets.txt
lrwxrwxrwx 1 elliot elliot 9 May 8 22:02 soft.txt -> facts.txt
-rw-rw-r-- 1 elliot elliot 212 May 12 05:09 upper.txt

现在,在我们做一些字符类的示例之前,让我们创建以下四个文件:

elliot@ubuntu-linux:~$ touch One TWO 7wonders GTA1

现在,如果您想列出以大写字母结尾的文件名,您可以使用字符类[:upper:]如下:

elliot@ubuntu-linux:~$ ls -l *[[:upper:]]
-rw-rw-r-- 1 elliot elliot 0 May 12 18:14 TWO

请注意,字符类本身也被括号括起来。

如果您想列出以数字(数字)开头的文件名,您可以使用字符类[:digit:]如下:

elliot@ubuntu-linux:~$ ls -l [[:digit:]]*
-rw-rw-r-- 1 elliot elliot 0 May 12 18:14 7wonders

唯一匹配的是文件7wonders

正则表达式

到目前为止,我们一直在使用文件名的通配符。正则表达式(简称 Regex)是 Linux 的另一个功能,它允许您在文本文件中搜索特定的模式。正则表达式也经常与 grep 命令一起使用。

表 15 列出了最常见的正则表达式及其用途:

正则表达式 它的作用
* 匹配前面字符或表达式的零个或多个。
+ 匹配前面字符或表达式的一个或多个。
. 匹配任何单个字符。与 ? 通配符相同。
^ 匹配行首的后面表达式。例如,^dog 将匹配所有以单词 dog 开头的行。
` 正则表达式
--- ---
* 匹配前面字符或表达式的零个或多个。
+ 匹配前面字符或表达式的一个或多个。
. 匹配任何单个字符。与 ? 通配符相同。
^ 匹配行首的后面表达式。例如,^dog 将匹配所有以单词 dog 开头的行。
| 匹配行尾的前面表达式。例如,bird$ 将匹配所有以单词 bird 结尾的行。
\ 用作转义字符,以匹配反斜杠后面的特殊字符。例如,\* 匹配星号(*)。
[characters] 匹配属于字符集字符的字符。例如,[abc] 将匹配字符 abc
[^characters] 匹配不属于字符集字符的任何字符。基本上是对 [characters] 的否定。例如,[!abc] 将匹配不是 abc 的任何字符。
{x,y} 匹配前面表达式的 x 到 y 次出现。
{x} 匹配前面表达式的 x 次出现。
{x,} 匹配前面表达式的 x 次或更多出现。
{,x} 匹配前面表达式的最多 x 次出现。

表 15:正则表达式

嗯,这是一个很长的正则表达式列表。让我们练习一下。创建一个名为 practice.txt 的文件,其中包含以下文本:

111222333
my cell number is 123-456-789\. 
you are a smart man
man is a linux command. 
man ... oh man.
dog is a cute pet. 
g
dg 
ddg 
dddg
Two stars ** 
tan
tantan 
tantantan

要在 grep 命令中使用正则表达式,可以使用 -E 选项或 egrep 命令。egrep 命令只是 grep -E 的别名。

现在,请注意 *** 正则表达式与 *** 通配符是不同的。要了解区别,请运行以下命令:

elliot@ubuntu-linux:~$ egrep d*g practice.txt

这将给出以下输出:

图 1:* 正则表达式

注意,d*g 没有匹配单词 dog;相反,它匹配了:

  • g(d 的零次出现)

  • dg(d 的一次出现)

  • ddg(d 的两次出现)

  • dddg(d 的三次出现)

这是因为 ** *regex 匹配前面字符或表达式的零个或多个,而 *** 通配符 匹配任何字符。

现在,要匹配一个或多个出现的 d 后跟 g,可以使用正则表达式 d+g

elliot@ubuntu-linux:~$ egrep d+g practice.txt 
dg
ddg 
dddg

要匹配特殊字符 *,可以在单引号或双引号之间使用反斜杠,如下所示:

elliot@ubuntu-linux:~$ egrep "\*" practice.txt 
Two stars **

要匹配包含字母 m 后跟任何单个字符,然后是字母 n 的任何模式,可以运行:

elliot@ubuntu-linux:~$ egrep m.n practice.txt 
you are a smart man
man is a linux command. 
man ... oh man.

要匹配以单词 man 开头的行,可以运行:

elliot@ubuntu-linux:~$ egrep ^man practice.txt 
man is a linux command.
man ... oh man.

要匹配以单词 man 结尾的行,可以运行:

elliot@ubuntu-linux:~$ egrep man$ practice.txt 
you are a smart man

您也可以使用字符类。例如,要搜索包含至少一个数字的所有行,可以运行:

elliot@ubuntu-linux:~$ egrep "[[:digit:]]{1,}" practice.txt 
111222333
my cell number is 123-456-789.

您还可以搜索特定模式,如电话号码:

elliot@ubuntu-linux:~$ egrep "[[:digit:]]{3}-[[:digit:]]{3}-[[:digit:]]{3}" 
practice.txt
my cell number is 123-456-789.

这将搜索包含三个数字后跟一个破折号,然后是另外三个数字后跟另一个破折号,然后是另外三个数字的行。

我知道你认为 regex 很复杂,很难记住所有这些,你是对的!这就是为什么有一个包含我们讨论的所有正则表达式的 man 页面:

elliot@ubuntu-linux:~$ man regex

此外,grep man 页面包括对本章讨论的所有正则表达式的解释。

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 显示文件 /etc/hostname 的大小(以字节为单位)。

  2. 仅显示文件 /etc/group 中的组名。

  3. 显示文件 /etc/services 中的总行数。

  4. 仅显示文件 /etc/passwd 中包含单词 "bash" 的行。

  5. 显示uptime命令的输出为全大写字母。

让我们玩寻找游戏

我们有时都会忘记放东西的地方;我总是忘记放我的钱包和保存文件的位置。我很确定你也会忘记放文件的位置,因此在本章中,您将学习两种不同的搜索和定位文件的方法。

第十二章:locate 命令

如果您知道文件的名称,但不确定文件的位置,您可以使用locate命令获取文件的路径。

locate命令在预先构建的文件数据库中搜索文件位置,因此在使用locate命令之前更新文件数据库至关重要。如果您不更新数据库,locate命令可能无法检索新创建文件的位置。

更新文件数据库

要更新文件数据库,您必须以 root 用户身份运行updatedb命令:

root@ubuntu-linux:~# updatedb

updatedb命令不会显示任何输出。

现在,假设我们忘记了文件facts.txt的位置,我们不记得它在哪里;在这种情况下,您可以运行locate命令,然后跟上文件名:

root@ubuntu-linux:~# locate facts.txt
/home/elliot/facts.txt
/var/facts.txt

哇!它显示了文件facts.txt的位置。

现在我将向您展示如果搜索新创建的文件而不更新文件数据库会发生什么。

/home目录中创建一个名为ghost.txt的空文件:

root@ubuntu-linux:/# touch /home/ghost.txt

现在尝试搜索文件ghost.txt

root@ubuntu-linux:/# locate ghost.txt 
root@ubuntu-linux:/#

locate命令找不到它!为什么?........那是因为您创建了一个新文件,文件数据库还不知道它。您必须先运行updatedb命令来更新文件数据库:

root@ubuntu-linux:/# updatedb 
root@ubuntu-linux:/# locate ghost.txt
/home/ghost.txt

是的!更新文件数据库后,locate命令现在可以获取文件ghost.txt的位置。

您还可以使用locate命令来使用通配符。例如,locate *.log将搜索系统中的所有日志文件。您还可以使用-r选项在搜索中启用regex

find 命令

find命令是您可以在 Linux 中用于搜索文件的更强大的命令。与locate命令不同,find命令实时运行,因此您无需更新任何文件数据库。find命令的一般语法如下:

find [starting-point(s)] [options] [expression]

find命令将在您指定的每个起点(目录)下搜索。

例如,要在您的/home目录下搜索所有.txt文件,您可以运行:

root@ubuntu-linux:~# find /home -name "*.txt"
/home/elliot/facts2.txt
/home/elliot/dir1/directory2/file1.txt
/home/elliot/dir1/directory2/file3.txt
/home/elliot/dir1/directory2/file2.txt
/home/elliot/soft.txt
/home/elliot/facts.txt
/home/elliot/practise.txt
/home/elliot/upper.txt
/home/elliot/mydate.txt
/home/elliot/all.txt
/home/elliot/Mars.txt
/home/elliot/output.txt
/home/elliot/planets.txt
/home/elliot/error.txt
/home/elliot/animals.txt
/home/ghost.txt

-name选项搜索文件名;您可以在find命令中使用许多其他选项。

-type选项搜索文件类型;例如,要在/home/elliot/dir1中搜索所有目录,您可以运行:

root@ubuntu-linux:~# find /home/elliot/dir1 -type d
/home/elliot/dir1
/home/elliot/dir1/cities
/home/elliot/dir1/directory2

请注意,它只列出了/home/elliot/dir1中的目录。要列出常规文件,您可以运行:

root@ubuntu-linux:~# find /home/elliot/dir1 -type f
/home/elliot/dir1/cities/paris
/home/elliot/dir1/cities/london
/home/elliot/dir1/cities/berlin
/home/elliot/dir1/directory2/file1.txt
/home/elliot/dir1/directory2/file3.txt
/home/elliot/dir1/directory2/file2.txt

要搜索常规文件和目录,您可以使用逗号:

root@ubuntu-linux:~# find /home/elliot/dir1 -type d,f
/home/elliot/dir1
/home/elliot/dir1/cities
/home/elliot/dir1/cities/paris
/home/elliot/dir1/cities/london
/home/elliot/dir1/cities/berlin
/home/elliot/dir1/directory2
/home/elliot/dir1/directory2/file1.txt
/home/elliot/dir1/directory2/file3.txt
/home/elliot/dir1/directory2/file2.txt

现在,以 root 用户身份在/root中创建两个文件large.txtLARGE.TXT

root@ubuntu-linux:~# touch large.txt LARGE.TXT

假设您忘记了这两个文件的位置;在这种情况下,您可以使用/作为起点:

root@ubuntu-linux:~# find / -name large.txt
/root/large.txt

请注意,它只列出了large.txt的位置。如果您还想要另一个文件LARGE.TXT怎么办?在这种情况下,您可以使用-iname选项,使搜索不区分大小写:

root@ubuntu-linux:~# find / -iname large.txt
/root/LARGE.TXT
/root/large.txt

让我们将行“12345”附加到文件large.txt中:

root@ubuntu-linux:~# echo 12345 >> large.txt

请注意文件large.txtLARGE.txt的大小:

root@ubuntu-linux:~# du -b large.txt LARGE.TXT
6 large.txt
0 LARGE.TXT

文件LARGE.TXT的大小为零字节,因为它是空的。您可以使用-size选项根据文件大小搜索文件。

例如,要在/root目录下搜索空文件,您可以运行以下命令:

root@ubuntu-linux:~# find /root -size 0c
/root/LARGE.TXT

如您所见,它列出了LARGE.TXT,因为它有零个字符;0c表示零个字符(或字节)。现在,如果您想在/root下搜索大小为6字节的文件,您可以运行:

root@ubuntu-linux:~# find /root -size 6c
/root/large.txt

如您所见,它列出了文件large.txt

您甚至可以在搜索中使用大小范围;Table 16向您展示了使用find命令的大小范围的一些示例。

命令 作用
find / -size +100M 将搜索所有大于100 MB 的文件。
find / -size -5c 将搜索所有小于5字节的文件。
find / -size +50M -size -100M 将搜索所有大于50 MB 但小于100 MB 的文件。
find / -size +1G 将搜索所有大于1 GB 的文件。

表 16:使用大小范围

-mtime-atime选项根据修改和访问时间搜索文件。-exec也是一个有用的命令选项,允许您对find结果运行另一个命令。

例如,您可以通过运行以下命令在/root中对所有空文件进行长列表:

root@ubuntu-linux:~# find /root -size 0c -exec ls -l {} +
-rw-r--r-- 1 root root 0 May 16 14:31 /root/LARGE.TXT

很多人在使用-exec选项时忘记包括{} +{} +引用了在查找结果中找到的所有文件。

您可以在-exec选项中使用任何命令。例如,您可能希望删除从查找结果中获得的文件,而不是进行长列表。在这种情况下,您可以运行:

root@ubuntu-linux:~# find /root -size 0c -exec rm {} +

现在文件LARGE.TXT已被删除:

root@ubuntu-linux:~# ls -l LARGE.TXT
ls: cannot access 'LARGE.TXT': No such file or directory

我强烈建议您阅读man页面,以探索可以使用的众多其他选项。

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 使用locate命令找到文件boot.log的路径。

  2. 查找所有大小大于50 MB 的文件。

  3. 查找所有大小在70 MB 和100 MB 之间的文件。

  4. 查找所有属于用户smurf的文件。

  5. 查找所有属于组developers的文件。

您得到了一个软件包

在本章中,您将学习如何在 Linux 系统上管理软件应用程序。您将学习如何使用 Debian 软件包管理器来下载、安装、删除、搜索和更新软件包。

第十三章:什么是软件包?

在 Linux 中,软件包是一个压缩的存档文件,其中包含特定软件应用程序运行所需的所有必要文件。例如,像 Firefox 这样的网络浏览器以一个包的形式提供,其中包含了 Firefox 运行所需的所有文件。

软件包管理器的作用

软件包管理器是我们在 Linux 中用来管理软件包的程序;也就是说,下载、安装、删除、搜索和更新软件包。请记住,不同的 Linux 发行版有不同的软件包管理器。例如,dpkg代表 Debian 软件包管理器,是 Ubuntu 和其他基于 Debian 的 Linux 发行版的软件包管理器。另一方面,基于 RedHat 的 Linux 发行版如 Fedora 和 CentOS 使用rpm,代表 RedHat 软件包管理器。其他 Linux 发行版如 SUSE 使用zypper作为软件包管理器等等。

软件包从哪里来?

很少有经验丰富的 Linux 用户会像 Windows 或 macOS 用户那样去网站下载软件包。相反,每个 Linux 发行版都有其软件包来源列表,大部分软件包都来自这些来源。这些来源也被称为存储库。以下图示了在您的 Linux 系统上下载软件包的过程:

图 1:软件包存储在存储库中。请注意,软件包存储在多个存储库中

如何下载软件包

在 Ubuntu 和其他 Debian Linux 发行版上,您可以使用命令行实用程序apt-get来管理软件包。在幕后,apt-get利用软件包管理器dpkg。要下载软件包,您可以运行命令apt-get download后跟软件包名称:

apt-get download package_name

作为root用户,切换到/tmp目录:

root@ubuntu-linux:~# cd /tmp

要下载cmatrix软件包,您可以运行以下命令:

root@ubuntu-linux:/tmp# apt-get download cmatrix
Get:1 http://ca.archive.ubuntu.com/ubuntu bionic/universe amd64 cmatrix amd64
1.2a-5build3 [16.1 kB]
Fetched 16.1 kB in 1s (32.1 kB/s) 

cmatrix软件包将被下载到/tmp目录中:

root@ubuntu-linux:/tmp# ls 
cmatrix_1.2a-5build3_amd64.deb

请注意软件包名称中的.deb扩展名,这表示它是一个 Debian 软件包。在 RedHat 发行版上,软件包名称以.rpm扩展名结尾。您可以通过运行以下命令dpkg -c来列出cmatrix软件包中的文件:

root@ubuntu-linux:/tmp# dpkg -c cmatrix_1.2a-5build3_amd64.deb
drwxr-xr-x root/root     0 2018-04-03 06:17 ./
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/bin/
-rwxr-xr-x root/root 18424 2018-04-03 06:17 ./usr/bin/cmatrix
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/share/
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/share/consolefonts/
-rw-r--r-- root/root  4096 1999-05-13 08:55 ./usr/share/consolefonts/matrix.fnt
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/share/doc/
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/share/doc/cmatrix/
-rw-r--r-- root/root  2066 2000-04-03 19:29 ./usr/share/doc/cmatrix/README
-rw-r--r-- root/root   258 1999-05-13 09:12 ./usr/share/doc/cmatrix/TODO
-rw-r--r-- root/root  1128 2018-04-03 06:17 ./usr/share/doc/cmatrix/copyright
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/share/man/
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/share/man/man1/
-rw-r--r-- root/root   932 2018-04-03 06:17 ./usr/share/man/man1/cmatrix.1.gz
drwxr-xr-x root/root     0 2018-04-03 06:17 ./usr/share/menu/
-rw-r--r-- root/root   392 2018-04-03 06:17 ./usr/share/menu/cmatrix

请注意,我们只下载了软件包,但尚未安装。如果您运行cmatrix命令,将不会发生任何事情:

root@ubuntu-linux:/tmp# cmatrix
bash: /usr/bin/cmatrix: No such file or directory

如何安装软件包

您可以使用dpkg命令的-i选项来安装已下载的软件包:

root@ubuntu-linux:/tmp# dpkg -i cmatrix_1.2a-5build3_amd64.deb 
Selecting previously unselected package cmatrix.
(Reading database ... 178209 files and directories currently installed.) Preparing to unpack cmatrix_1.2a-5build3_amd64.deb ...
Unpacking cmatrix (1.2a-5build3) ... 
Setting up cmatrix (1.2a-5build3) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ... 
root@ubuntu-linux:/tmp#

就是这样!现在运行cmatrix命令:

root@ubuntu-linux:/tmp# cmatrix

您将在终端上看到矩阵运行,就像下图中一样:

图 2:cmatrix

我们已经采取了安装cmatrix软件包的长途旅程。我们首先下载了软件包,然后安装了它。您可以通过运行命令apt-get install后跟软件包名称来立即安装软件包(无需下载):

apt-get install package_name

例如,您可以通过运行以下命令安装GNOME Chess游戏:

root@ubuntu-linux:/tmp# apt-get install gnome-chess 
Reading package lists... Done
Building dependency tree
Reading state information... Done 
Suggested packages:
 bbchess crafty fairymax fruit glaurung gnuchess phalanx sjeng stockfish toga2 
The following NEW packages will be installed:
 gnome-chess
0 upgraded, 1 newly installed, 0 to remove and 357 not upgraded. 
Need to get 0 B/1,514 kB of archives.
After this operation, 4,407 kB of additional disk space will be used. 
Selecting previously unselected package gnome-chess.
(Reading database ... 178235 files and directories currently installed.) Preparing to unpack .../gnome-chess_1%3a3.28.1-1_amd64.deb ...
Unpacking gnome-chess (1:3.28.1-1) ...
Processing triggers for mime-support (3.60ubuntu1) ...
Processing triggers for desktop-file-utils (0.23-1ubuntu3.18.04.2) ... 
Processing triggers for libglib2.0-0:amd64 (2.56.3-0ubuntu0.18.04.1) ... 
Setting up gnome-chess (1:3.28.1-1) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ... 
Processing triggers for gnome-menus (3.13.3-11ubuntu1.1) ... 
Processing triggers for hicolor-icon-theme (0.17-2) ...

现在您可以通过运行gnome-chess命令来启动游戏:

root@ubuntu-linux:/tmp# gnome-chess

图 3:GNOME Chess

如何删除软件包

您可以通过运行命令apt-get remove后跟软件包名称来轻松删除一个软件包:

apt-get remove package_name

例如,如果你厌倦了矩阵生活方式,并决定删除cmatrix软件包,你可以运行:

root@ubuntu-linux:/tmp# apt-get remove cmatrix 
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages will be REMOVED:
 cmatrix
0 upgraded, 0 newly installed, 1 to remove and 357 not upgraded. 
After this operation, 49.2 kB disk space will be freed.
Do you want to continue? [Y/n] y
(Reading database ... 178525 files and directories currently installed.) 
Removing cmatrix (1.2a-5build3) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...

现在,如果您运行cmatrix命令,您将收到一个错误:

root@ubuntu-linux:/tmp# cmatrix
Command 'cmatrix' not found, but can be installed with: 
apt install cmatrix

apt-get remove命令会删除(卸载)一个软件包,但不会删除软件包的配置文件。您可以使用apt-get purge命令来删除软件包以及其配置文件。

例如,如果您想删除gnome-chess软件包以及其配置文件,您可以运行:

root@ubuntu-linux:/tmp# apt-get purge gnome-chess 
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following package was automatically installed and is no longer required:    
  hoichess
Use 'apt autoremove' to remove it.
The following packages will be REMOVED:
 gnome-chess*
0 upgraded, 0 newly installed, 1 to remove and 357 not upgraded. 
After this operation, 4,407 kB disk space will be freed.
Do you want to continue? [Y/n] y
(Reading database ... 178515 files and directories currently installed.) 
Removing gnome-chess (1:3.28.1-1) ...
Processing triggers for mime-support (3.60ubuntu1) ...
Processing triggers for desktop-file-utils (0.23-1ubuntu3.18.04.2) ... 
Processing triggers for libglib2.0-0:amd64 (2.56.3-0ubuntu0.18.04.1) ... Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Processing triggers for gnome-menus (3.13.3-11ubuntu1.1) ... 
Processing triggers for hicolor-icon-theme (0.17-2) ...
(Reading database ... 178225 files and directories currently installed.) 
Purging configuration files for gnome-chess (1:3.28.1-1) ...

您甚至可以在输出的最后一行中看到Purging configuration files for gnome-chess (1:3.28.1-1) ...,这意味着gnome-chess的配置文件也正在被删除。

如何搜索软件包

有时您不确定软件包名称。在这种情况下,您无法安装它,直到查找它。您可以使用apt-cache search命令,后跟搜索词或关键词来搜索软件包:

apt-cache search keyword

例如,假设您想安装wireshark软件包,但您只记得软件包名称中有shark这个词。在这种情况下,您可以运行以下命令:

root@ubuntu-linux:/tmp# apt-cache search shark
dopewars - drug-dealing game set in streets of New York City
dopewars-data - drug-dealing game set in streets of New York City - data files forensics-extra - Forensics Environment - extra console components (metapackage) kernelshark - Utilities for graphically analyzing function tracing in the kernel libcrypto++-dev - General purpose cryptographic library - C++ development libshark-dev - development files for Shark
libshark0 - Shark machine learning library
libwireshark-data - network packet dissection library -- data files 
libwireshark-dev - network packet dissection library -- development files libwireshark10 - network packet dissection library -- shared library 
libwiretap-dev - network packet capture library -- development files
libwsutil-dev - network packet dissection utilities library -- development files libwsutil8 - network packet dissection utilities library -- shared library netmate - netdude clone that shows pcap dump lines in network header style plowshare-modules - plowshare drivers for various file sharing websites
shark-doc - documentation for Shark
tcpxtract - extract files from network traffic based on file signatures 
tshark - network traffic analyzer - console version
wifite - Python script to automate wireless auditing using aircrack-ng tools wireshark - network traffic analyzer - meta-package
wireshark-common - network traffic analyzer - common files 
wireshark-dev - network traffic analyzer - development tools 
wireshark-doc - network traffic analyzer - documentation 
wireshark-gtk - network traffic analyzer - GTK+ version 
wireshark-qt - network traffic analyzer - Qt version
zeitgeist-explorer - GUI application for monitoring and debugging zeitgeist forensics-extra-gui - Forensics Environment - extra GUI components (metapackage) horst - Highly Optimized Radio Scanning Tool
libvirt-wireshark - Wireshark dissector for the libvirt protocol 
libwiretap7 - network packet capture library -- shared library 
libwscodecs1 - network packet dissection codecs library -- shared library minetest-mod-animals - Minetest mod providing animals
nsntrace - perform network trace of a single process by using network namespaces libwireshark11 - network packet dissection library -- shared library 
libwiretap8 - network packet capture library -- shared library
libwscodecs2 - network packet dissection codecs library -- shared library libwsutil9 - network packet dissection utilities library -- shared library

然后您会被大量输出淹没,列出所有软件包名称中包含shark这个词的软件包。我敢打赌您可以在输出的中间找到wireshark软件包。我们可以通过使用-n选项获得一个更短和精炼的输出:

root@ubuntu-linux:/tmp# apt-cache -n search shark
kernelshark - Utilities for graphically analyzing function tracing in the kernel libshark-dev - development files for Shark
libshark0 - Shark machine learning library
libwireshark-data - network packet dissection library -- data files 
libwireshark-dev - network packet dissection library -- development files
libwireshark10 - network packet dissection library -- shared library 
shark-doc - documentation for Shark
tshark - network traffic analyzer - console version 
wireshark - network traffic analyzer - meta-package 
wireshark-common - network traffic analyzer - common files 
wireshark-dev - network traffic analyzer - development tools 
wireshark-doc - network traffic analyzer - documentation 
wireshark-gtk - network traffic analyzer - GTK+ version 
wireshark-qt - network traffic analyzer - Qt version
libndpi-wireshark - extensible deep packet inspection library - wireshark dissector 
libvirt-wireshark - Wireshark dissector for the libvirt protocol
libwireshark11 - network packet dissection library -- shared library

这将只列出软件包名称中包含shark这个词的软件包。现在,您可以通过运行以下命令来安装wireshark

root@ubuntu-linux:/tmp# apt-get install wireshark

如何显示软件包信息

要查看软件包信息,您可以使用apt-cache show命令,后跟软件包名称:

apt-cache show package_name

例如,要显示cmatrix软件包信息,您可以运行:

root@ubuntu-linux:~# apt-cache show cmatrix 
Package: cmatrix
Architecture: amd64 
Version: 1.2a-5build3 
Priority: optional 
Section: universe/misc 
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> 
Original-Maintainer: Diego Fernández Durán <diego@goedi.net>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug 
Installed-Size: 48
Depends: libc6 (>= 2.4), libncurses5 (>= 6), libtinfo5 (>= 6) 
Recommends: kbd
Suggests: cmatrix-xfont
Filename: pool/universe/c/cmatrix/cmatrix_1.2a-5build3_amd64.deb 
Size: 16084
MD5sum: 8dad2a99d74b63cce6eeff0046f0ac91 
SHA1: 3da3a0ec97807e6f53de7653e4e9f47fd96521c2
SHA256: cd50212101bfd71479af41e7afc47ea822c075ddb1ceed83895f8eaa1b79ce5d Homepage: http://www.asty.org/cmatrix/
Description-en_CA: simulates the display from "The Matrix" 
Screen saver for the terminal based in the movie "The Matrix".
 * Support terminal resize.
 * Screen saver mode: any key closes it.
 * Selectable color.
 * Change text scroll rate.
Description-md5: 9af1f58e4b6301a6583f036c780c6ae6

您可以在输出中看到许多有用的信息,包括软件包描述和软件包维护者的联系信息,如果您发现错误并希望报告它,则这些信息很有用。您还将了解软件包是否依赖于其他软件包。

软件包依赖可能会变成一场噩梦,因此我强烈建议您尽可能使用apt-get install命令来安装软件包,因为它在安装软件包时会检查和解决软件包依赖关系。另一方面,dpkg -i命令不会检查软件包依赖关系。记住这一点!

您可以使用apt-cache depends命令来列出软件包依赖关系:

apt-cache depends package_name

例如,要查看为使cmatrix正常工作而需要安装的软件包列表,您可以运行以下命令:

root@ubuntu-linux:~# apt-cache depends cmatrix 
cmatrix
 Depends: libc6 
 Depends: libncurses5 
 Depends: libtinfo5 
 Recommends: kbd 
 Suggests: cmatrix-xfont

正如您所看到的,cmatrix软件包依赖于三个软件包:

  • libc6

  • libncurses5

  • libtinfo5

这三个软件包必须安装在系统上,以便cmatrix正常运行。

列出所有软件包

您可以使用dpkg -l命令来列出系统上安装的所有软件包:

root@ubuntu-linux:~# dpkg -l

您还可以使用apt-cache pkgnames命令来列出所有可供您安装的软件包:

root@ubuntu-linux:~# apt-cache pkgnames 
libdatrie-doc
libfstrcmp0-dbg 
libghc-monadplus-doc 
librime-data-sampheng 
python-pyao-dbg 
fonts-georgewilliams
python3-aptdaemon.test 
libcollada2gltfconvert-dev 
python3-doc8
r-bioc-hypergraph
.
.
.
.
.

您可以将输出导入到wc -l命令中,以获得可用软件包的总数:

root@ubuntu-linux:~# apt-cache pkgnames | wc -l 64142

哇!这是一个庞大的数字;我的系统上有超过 64,000 个可用软件包。

您可能还想知道您的系统用于获取所有这些软件包的存储库(源)。这些存储库包含在文件/etc/ap- t/sources.list中,以及在目录/etc/apt/- sources.list.d/下具有后缀.list的任何文件中。您可以查看man页面:

root@ubuntu-linux:~# man sources.list

了解如何向系统添加存储库。

您还可以使用apt-cache policy命令来列出系统上启用的所有存储库:

root@ubuntu-linux:~# apt-cache policy 
Package files:
100 /var/lib/dpkg/status 
    release a=now
500 http://dl.google.com/linux/chrome/deb stable/main amd64 
    Packages release v=1.0,o=Google LLC,a=stable,n=stable,l=Google,c=main,
    b=amd64 origin dl.google.com
100 http://ca.archive.ubuntu.com/ubuntu bionic-backports/main i386 
    Packages release v=18.04,o=Ubuntu,a=bionic-backports,n=bionic,l=Ubuntu,
    c=main,b=i386 origin ca.archive.ubuntu.com
100 http://ca.archive.ubuntu.com/ubuntu bionic-backports/main amd64 
    Packages release v=18.04,o=Ubuntu,a=bionic-backports,n=bionic,l=Ubuntu,
    c=main,b=amd64 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/multiverse i386 
    Packages release v=18.04,o=Ubuntu,a=bionic,n=bionic,
    l=Ubuntu,c=multiverse,b=i386 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/multiverse amd64 
    Packages release v=18.04,o=Ubuntu,a=bionic,n=bionic,l=Ubuntu,
    c=multiverse,b=amd64 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/universe i386 
    Packages release v=18.04,o=Ubuntu,a=bionic,n=bionic,l=Ubuntu,
    c=universe,b=i386 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/universe amd64 
    Packages release v=18.04,o=Ubuntu,a=bionic,n=bionic,l=Ubuntu,
    c=universe,b=amd64 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/restricted i386 
    Packages release v=18.04,o=Ubuntu,a=bionic,n=bionic,l=Ubuntu,
    c=restricted,b=i386 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/restricted amd64 
    Packages release v=18.04,o=Ubuntu,a=bionic,n=bionic,l=Ubuntu,
    c=restricted,b=amd64 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/main i386 
    Packages release v=18.04,o=Ubuntu,a=bionic,
    n=bionic,l=Ubuntu,c=main,b=i386 origin ca.archive.ubuntu.com
500 http://ca.archive.ubuntu.com/ubuntu bionic/main amd64 
    Packages release v=18.04,o=Ubuntu,a=bionic,n=bionic,
    l=Ubuntu,c=main,b=amd64 origin ca.archive.ubuntu.com
Pinned packages:

如果您渴望知道哪个存储库提供了特定的软件包,您可以使用apt-cache policy命令,后跟软件包名称:

apt-cache policy package_name

例如,要知道哪个存储库提供了cmatrix软件包,您可以运行:

root@ubuntu-linux:~# apt-cache policy cmatrix 
cmatrix:
 Installed: 1.2a-5build3 
 Candidate: 1.2a-5build3 
 Version table:
*** 1.2a-5build3 500
 500 http://ca.archive.ubuntu.com/ubuntu bionic/universe amd64 Packages
 100 /var/lib/dpkg/status

从输出中,您可以看到cmatrix软件包来自于ca.archive.ubuntu.com/ubuntu的 bionic/universe 存储库。

修补您的系统

如果某个软件包有可用的更新版本,那么您可以使用apt-get install --only-upgrade命令,后跟软件包名称来升级它:

apt-get install --only-upgrade package_name

例如,您可以通过运行以下命令来升级nano软件包:

root@ubuntu-linux:~# apt-get install --only-upgrade nano 
Reading package lists... Done
Building dependency tree
Reading state information... Done
nano is already the newest version (2.9.3-2).
The following package was automatically installed and is no longer required: 
 hoichess
Use 'apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 357 not upgraded.

您还可以通过运行以下命令来升级系统上安装的所有软件包:

  1. apt-get update

  2. apt-get upgrade

第一个命令apt-get update将更新可用软件包及其版本的列表,但不会进行任何安装或升级:

root@ubuntu-linux:~# apt-get update
Ign:1 http://dl.google.com/linux/chrome/deb stable InRelease 
Hit:2 http://ca.archive.ubuntu.com/ubuntu bionic InRelease
Hit:3 http://ppa.launchpad.net/linuxuprising/java/ubuntu bionic InRelease 
Hit:4 http://dl.google.com/linux/chrome/deb stable Release
Hit:5 http://security.ubuntu.com/ubuntu bionic-security InRelease 
Hit:6 http://ca.archive.ubuntu.com/ubuntu bionic-updates InRelease 
Hit:8 http://ca.archive.ubuntu.com/ubuntu bionic-backports InRelease 
Reading package lists... Done

第二个命令apt-get upgrade将升级系统上安装的所有软件包:

root@ubuntu-linux:~# apt-get upgrade 
Reading package lists... Done 
Building dependency tree
Reading state information... Done 
Calculating upgrade... Done
The following package was automatically installed and is no longer required: 
 hoichess
Use 'apt autoremove' to remove it.
The following packages have been kept back:
 gstreamer1.0-gl libcogl20 libgail-3-0 libgl1-mesa-dri libgstreamer-gl1.0-0 
 libreoffice-calc libreoffice-core libreoffice-draw libreoffice-gnome 
    libreoffice-gtk3 
 libwayland-egl1-mesa libxatracker2 linux-generic linux-headers-generic
 software-properties-common software-properties-gtk ubuntu-desktop 
The following packages will be upgraded:
 apt apt-utils aptdaemon aptdaemon-data aspell base-files bash bind9-host bluez 
 python2.7-minimal python3-apt python3-aptdaemon python3-aptdaemon.gtk3widgets 
 python3-problem-report python3-update-manager python3-urllib3 python3.6
342 upgraded, 0 newly installed, 0 to remove and 30 not upgraded. 
Need to get 460 MB of archives.
After this operation, 74.3 MB of additional disk space will be used. 
Do you want to continue? [Y/n]

请记住顺序很重要;也就是说,在运行apt-get upgrade命令之前,您需要运行apt-get update命令。

在 Linux 术语中,升级系统上安装的所有软件包的过程称为打补丁系统

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 在您的系统上安装tmux软件包。

  2. 列出vim软件包的所有依赖项。

  3. 在您的系统上安装cowsay软件包。

  4. 删除cowsay软件包以及其所有配置文件。

  5. 升级系统上的所有软件包(打补丁您的系统)。

杀死进程

在您的系统上运行的任何程序都是一个进程。在本章中,您将学习有关 Linux 进程的所有内容。您将学习如何查看进程信息。您还将学习如何向进程发送不同的信号。此外,您将了解前台和后台进程之间的区别。

第十四章:什么是进程?

进程只是运行程序的一个实例。因此,您系统上运行的任何程序都是一个进程。以下都是进程的例子:

  • 在您的系统上运行的 Firefox 或任何网络浏览器都是一个进程。

  • 您正在运行的终端现在就是一个进程。

  • 您在系统上玩的任何游戏都是一个进程。

  • 复制文件是一个进程。

就像文件一样,每个进程都由特定用户拥有。进程的所有者只是启动该进程的用户。

要列出所有由特定用户拥有的进程,您可以运行命令ps -u后跟用户名:

ps -u username

例如,要列出所有由elliot拥有的进程,您可以运行:

root@ubuntu-linux:~# ps -u elliot
 PID TTY       TIME CMD
1365 ?     00:00:00 systemd
1366 ?     00:00:00 (sd-pam)
1379 ?     00:00:00 gnome-keyring-d
1383 tty2  00:00:00 gdm-x-session
1385 tty2  00:00:18 Xorg
1389 ?     00:00:00 dbus-daemon
1393 tty2  00:00:00 gnome-session-b
1725 ?     00:00:00 ssh-agent
1797 ?     00:00:00 gvfsd
. 
. 
. 
.

输出中的第一列列出了进程标识符PIDs)。PID 是一个唯一标识进程的数字,就像文件inodes一样。输出的最后一列列出了进程名称。

您可以使用ps -e命令列出系统上正在运行的所有进程:

root@ubuntu-linux:~# ps -e 
PID TTY     TIME  CMD
1  ?     00:00:01 systemd
2  ?     00:00:00 kthreadd
4  ?     00:00:00 kworker/0:0H
6  ?     00:00:00 mm_percpu_wq
7  ?     00:00:00 ksoftirqd/0
8  ?     00:00:00 rcu_sched
9  ?     00:00:00 rcu_bh
10 ?     00:00:00 migration/0
11 ?     00:00:00 watchdog/0
12 ?     00:00:00 cpuhp/0
13 ?     00:00:00 kdevtmpfs
.
.
.
.

您还可以使用-f选项来获取更多信息:

root@ubuntu-linux:~# ps -ef
UID    PID  PPID C STIME TTY    TIME    CMD
root      1    0 0 11:23    ? 00:00:01 /sbin/init splash
root      2    0 0 11:23    ? 00:00:00 [kthreadd]
root      4    2 0 11:23    ? 00:00:00 [kworker/0:0H]
root      6    2 0 11:23    ? 00:00:00 [mm_percpu_wq]
root      7    2 0 11:23    ? 00:00:00 [ksoftirqd/0]
root      8    2 0 11:23    ? 00:00:01 [rcu_sched]
root      9    2 0 11:23    ? 00:00:00 [rcu_bh]
root     10    2 0 11:23    ? 00:00:00 [migration/0]
elliot 1835 1393 1 11:25 tty2 00:00:58 /usr/bin/gnome-shell
elliot 1853 1835 0 11:25 tty2 00:00:00 ibus-daemon --xim --panel disable
elliot 1857 1365 0 11:25    ? 00:00:00 /usr/lib/gnome-shell/gnome-shell
elliot 1865 1853 0 11:25 tty2 00:00:00 /usr/lib/ibus/ibus-dconf
elliot 1868    1 0 11:25 tty2 00:00:00 /usr/lib/ibus/ibus-x11 --kill-daemon
elliot 1871 1365 0 11:25    ? 00:00:00 /usr/lib/ibus/ibus-portal
. 
. 
. 

输出的第一列列出了进程所有者的用户名。输出的第三列列出了父进程标识符PPID)。那么,父进程是什么?

父进程与子进程

父进程是启动了一个或多个子进程的进程。一个完美的例子将是您的终端和您的 bash shell;当您打开终端时,您的 bash shell 也会启动。

要获取进程的 PID,您可以使用pgrep命令后跟进程名称:

pgrep process_name

例如,要获取您的终端进程的 PID,您可以运行:

elliot@ubuntu-linux:~$ pgrep terminal 
10009

我的终端的 PID 是10009。现在,让我们获取 bash 进程的 PID:

elliot@ubuntu-linux:~$ pgrep bash 
10093

我的 bash shell 的 PID 是10093。现在,您可以使用-p选项后跟 bash PID 来获取您的 bash 进程的信息:

elliot@ubuntu-linux:~$ ps -fp 10093
UID     PID   PPID  C  STIME  TTY   TIME   CMD
elliot 10093 10009  0  13:37 pts/1 00:00:00 bash

您可以从输出中看到,我的 bash 进程的 PPID 等于我的终端进程的 PID。这证明了终端进程已启动了 bash 进程。在这种情况下,bash 进程被称为终端进程的子进程:

图 1:父进程与子进程

top命令是一个非常有用的命令,您可以使用它实时查看进程的信息。您可以查看其man页面以了解如何使用它:

elliot@ubuntu-linux:~$ man top 

上述命令的输出如下截图所示:

图 2:top 命令

前台与后台进程

Linux 中有两种类型的进程:

  • 前台进程

  • 后台进程

前台进程是附加到您的终端的进程。您必须等待前台进程完成,然后才能继续使用您的终端。

另一方面,后台进程是不附加到您的终端的进程,因此您可以在后台进程运行时使用您的终端。

yes命令会重复输出跟在其后的任何字符串,直到被杀死:

elliot@ubuntu-linux:~$ whatis yes
yes (1)               - output a string repeatedly until killed

例如,要在您的终端上重复输出单词hello,您可以运行命令:

elliot@ubuntu-linux:~$ yes hello 
hello
hello 
hello 
hello 
hello 
hello 
hello 
hello 
hello 
hello
.
.
.

请注意,它将继续运行,您无法在终端上执行其他操作;这是前台进程的一个典型例子。要收回您的终端,您需要杀死该进程。您可以通过按以下Ctrl + C键组合来杀死该进程:

hello 
hello 
hello 
hello 
hello
^C
elliot@ubuntu-linux:~$

一旦你按下Ctrl + C,进程将被终止,你可以继续使用你的终端。让我们做另一个例子;你可以使用firefox命令从你的终端启动 Firefox:

elliot@ubuntu-linux:~$ firefox

Firefox 浏览器将启动,但你将无法在终端上做任何事情直到关闭 Firefox;这是另一个前台进程的例子。现在,按下Ctrl + C来终止 Firefox 进程,这样你就可以重新使用你的终端了。

你可以通过添加&字符来将 Firefox 作为后台进程启动,如下所示:

elliot@ubuntu-linux:~$ firefox &
[1] 3468
elliot@ubuntu-linux:~$

Firefox 现在作为后台进程运行,你可以继续使用你的终端而不必关闭 Firefox。

向进程发送信号

你可以通过信号与进程进行交互和通信。有各种信号,每个信号都有不同的目的。要列出所有可用的信号,你可以运行kill -L命令:

elliot@ubuntu-linux:~$ kill -L
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX

注意到每个信号都有一个数字值。例如,19SIGSTOP信号的数字值。

为了了解信号的工作原理,让我们首先将 Firefox 作为后台进程启动:

elliot@ubuntu-linux:~$ firefox &
[1] 4218

注意到 Firefox 在我的系统上的 PID 是4218。我可以通过发送SIGKILL信号来终止 Firefox,如下所示:

elliot@ubuntu-linux:~$ kill -SIGKILL 4218
[1]+ Killed             firefox

这将立即关闭 Firefox。你也可以使用SIGKILL信号的数字值:

elliot@ubuntu-linux:~$ kill -9 4218

一般来说,kill命令的语法如下:

kill -SIGNAL PID

让我们再次将 Firefox 作为后台进程启动:

elliot@ubuntu-linux:~$ firefox & 
[1] 4907

注意到 Firefox 在我的系统上的 PID 是4907。现在继续在 Firefox 上播放 YouTube 视频。在你这样做之后,回到你的终端并向 Firefox 发送SIGSTOP信号:

elliot@ubuntu-linux:~$ kill -SIGSTOP 4907

你会注意到 Firefox 变得无响应,你的 YouTube 视频停止了;没问题 - 我们可以通过向 Firefox 发送SIGCONT信号来解决这个问题:

elliot@ubuntu-linux:~$ kill -SIGCONT 4907

这将使 Firefox 恢复,并且你的 YouTube 视频现在会继续播放。

到目前为止,你已经学会了三种信号:

  • SIGKILL:终止一个进程

  • SIGSTOP:停止一个进程

  • SIGCONT:继续一个进程

你可以使用pkill命令使用进程名称而不是进程标识符。例如,要关闭你的终端进程,你可以运行以下命令:

elliot@ubuntu-linux:~$ pkill -9 terminal

现在让我们做一些有趣的事情;打开你的终端并运行以下命令:

elliot@ubuntu-linux:~$ pkill -SIGSTOP terminal

哈哈!你的终端现在被冻结了。我会让你处理这个!

你可以向进程发送许多其他信号;查看以下man页面以了解每个信号的用途:

elliot@ubuntu-linux:~$ man signal

处理进程优先级

每个进程都有一个由友好度量表确定的优先级,范围从-2019。友好值越低,进程的优先级越高,所以友好值为-20给予进程最高的优先级。另一方面,友好值为19给予进程最低的优先级:

图 3:友好度量表

你可能会问自己:我们为什么关心进程的优先级?答案是效率!你的 CPU 就像一个繁忙餐厅里的服务员。一个高效的服务员会一直忙碌,确保所有顾客都得到满意的服务。同样,你的 CPU 分配时间给系统上运行的所有进程。优先级高的进程会得到 CPU 的更多关注。另一方面,优先级低的进程不会得到 CPU 的太多关注。

查看进程优先级

将 Firefox 作为后台进程启动:

elliot@ubuntu-linux:~$ firefox &
 [1] 6849

你可以使用ps命令查看进程的友好值:

elliot@ubuntu-linux:~$ ps -o nice -p 6849
NI
0

我的 Firefox 进程有一个友好值为0,这是默认值(平均优先级)。

为新进程设置优先级

你可以使用nice命令以你期望的优先级启动一个进程。nice命令的一般语法如下:

nice -n -20 →19 process

假设你要升级系统上的所有软件包;给这样一个进程尽可能高的优先级是明智的。为此,你可以以root用户身份运行以下命令:

root@ubuntu-linux:~# nice -n -20 apt-get upgrade

改变一个进程的优先级

您可以使用renice命令更改正在运行的进程的优先级。我们已经看到 Firefox 正在以默认进程优先级零运行;让我们更改 Firefox 的优先级,并将其设置为可能的最低优先级:

root@ubuntu-linux:~# renice -n 19 -p 6849
6849 (process ID) old priority 0, new priority 19

太棒了!现在我希望 Firefox 对我来说不会很慢;毕竟,我刚刚告诉我的 CPU 不要太关注 Firefox!

/proc 目录

Linux 中的每个进程都由/proc中的一个目录表示。例如,如果您的 Firefox 进程的 PID 为6849,那么目录/proc/6849将表示 Firefox 进程:

root@ubuntu-linux:~# pgrep firefox
6849
root@ubuntu-linux:~# cd /proc/6849
root@ubuntu-linux:/proc/6849#

在进程的目录中,您可以找到关于进程的许多有价值和富有洞察力的信息。例如,您将找到一个名为exe的软链接,指向进程的可执行文件:

root@ubuntu-linux:/proc/6849# ls -l exe
lrwxrwxrwx 1 elliot elliot 0 Nov 21 18:02 exe -> /usr/lib/firefox/firefox

您还会找到status文件,其中存储了有关进程的各种信息;这些信息包括进程状态、PPID、进程使用的内存量等等:

root@ubuntu-linux:/proc/6849# head status 
Name: firefox
Umask: 0022
State: S (sleeping) Tgid: 6849
Ngid: 0
Pid: 6849
PPid: 1990
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000

limits文件显示了为进程设置的当前限制:

root@ubuntu-linux:/proc/7882# cat limits
Limit                  Soft Limit   Hard Limit   Units
Max cpu time           unlimited    unlimited    seconds
Max file size          unlimited    unlimited    bytes
Max data size          unlimited    unlimited    bytes
Max stack size         8388608      unlimited    bytes
Max core file size     0            unlimited    bytes
Max resident set       unlimited    unlimited    bytes
Max processes          15599        15599        processes
Max open files         4096         4096         files
Max locked memory      16777216     16777216     bytes
Max address space      unlimited    unlimited    bytes
Max file locks         unlimited    unlimited    locks
Max pending signals    15599        15599        signals
Max msgqueue size      819200       819200       bytes
Max nice priority      0            0 
Max realtime priority  0            0 
Max realtime timeout   unlimited    unlimited    us

fd目录将显示进程当前在系统上正在使用的所有文件:

图 4:fd 目录

您还可以使用lsof命令列出进程当前正在使用的所有文件:

图 5:lsof 命令

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 列出您正在运行的终端的进程 ID。

  2. 列出您正在运行的终端的父进程 ID。

  3. 使用kill命令关闭您的终端。

  4. 将 Firefox 作为后台进程启动。

  5. 将 Firefox 的优先级更改为最高优先级。

Sudo 的威力

在本章中,您将学习如何为系统上的非 root 用户授予权限,以便他们可以运行特权命令。在现实生活中,系统管理员不应该将 root 密码给系统上的任何用户。但是,系统上的一些用户可能需要运行特权命令;现在的问题是:非 root 用户如何可以运行特权命令而不获取对系统的 root 访问权限?好吧,让我来告诉你!

第十五章:特权命令的示例

您会发现大多数需要 root 权限的命令在/sbin/usr/sbin目录中。让我们切换到用户smurf

elliot@ubuntu-linux:~$ su - smurf 
Password:
smurf@ubuntu-linux:~$

现在让我们看看smurf是否可以向系统添加新用户:

smurf@ubuntu-linux:~$ useradd bob 
useradd: Permission denied.

用户smurf收到了权限被拒绝的错误。那是因为useradd命令是一个特权命令。好吧!让我们尝试安装terminator软件包,我必须说这是一个非常酷的终端仿真器:

smurf@ubuntu-linux:~$ apt-get install terminator
E: Could not open lock file /var/lib/dpkg/lock-frontend - open
 (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), 
are you root?

再次!用户smurf遇到了错误。没有 root 权限生活就不好玩,我听到你在说。

使用 sudo 授予权限

用户smurf现在非常难过,因为他无法在系统上添加用户bob或安装terminator软件包。您可以使用visudo命令授予用户smurf运行他想要的两个特权命令的权限。

以 root 用户身份运行visudo命令:

root@ubuntu-linux:~# visudo

这将打开文件/etc/sudoers,以便您可以编辑它:

# This file MUST be edited with the 'visudo' command as root. 
#
# Please consider adding local content in /etc/sudoers.d/ instead of 
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file. 
#
Defaults           env_reset
Defaults           mail_badpass 
# Host alias specification 
# User alias specification 
# Cmnd alias specification
# User privilege specification 
root               ALL=(ALL:ALL) ALL
# Members of the admin group may gain root privileges
%admin             ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo              ALL=(ALL:ALL) ALL
# See sudoers(5) for more information on "#include" directives: 
#includedir /etc/sudoers.d

所有以井号字符开头的行都是注释,因此只关注这些行:

root   ALL=(ALL:ALL) ALL
%admin ALL=(ALL) ALL
%sudo  ALL=(ALL:ALL) ALL

第一行root ALL=(ALL:ALL) ALL是一个规则,授予用户root在系统上运行所有命令的权限。

现在我们可以添加一个规则,授予用户smurf运行useradd命令的权限。/etc/sudoers文件中规则的语法规范如下:

user hosts=(user:group) commands

现在将以下规则添加到/etc/sudoers文件中:

smurf    ALL=(ALL)       /usr/sbin/useradd

ALL关键字表示没有限制。请注意,您还必须包括命令的完整路径。现在,保存并退出文件,然后切换到用户smurf

root@ubuntu-linux:~# su - smurf 
smurf@ubuntu-linux:~$

现在在useradd命令之前加上sudo,如下所示:

smurf@ubuntu-linux:~$ sudo useradd bob 
[sudo] password for smurf: 
smurf@ubuntu-linux:~$ 

它将提示用户smurf输入密码;输入密码,就这样!用户bob已添加:

smurf@ubuntu-linux:~$ id bob
uid=1005(bob) gid=1005(bob) groups=1005(bob) 
smurf@ubuntu-linux:~$

酷!所以smurf现在可以向系统添加用户;但是,他仍然无法在系统上安装任何软件包:

smurf@ubuntu-linux:~$ sudo apt-get install terminator
Sorry, user smurf is not allowed to execute '/usr/bin/apt-get install 
terminator' as root on ubuntu-linux.

现在让我们来修复这个问题。切换回 root 用户,并运行visudo命令来编辑用户smurfsudo规则:

smurf ALL=(ALL) NOPASSWD: /usr/sbin/useradd, /usr/bin/apt-get install terminator

请注意,我还添加了NOPASSWD,这样smurf就不会被提示输入密码。我还添加了安装terminator软件包的命令。现在,保存并退出,然后切换回用户smurf,尝试安装terminator软件包:

smurf@ubuntu-linux:~$ sudo apt-get install terminator 
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required: 
 gsfonts-x11 java-common
Use 'sudo apt autoremove' to remove them. 
The following NEW packages will be installed:
 terminator

成功!请注意,sudo规则只授予smurf安装terminator软件包的权限。如果他尝试安装其他软件包,他将收到错误提示:

smurf@ubuntu-linux:~$ sudo apt-get install cmatrix
Sorry, user smurf is not allowed to execute '/usr/bin/apt-get install cmatrix' 
as root on ubuntu-linux.

用户和命令别名

您可以使用用户别名在/etc/sudoers文件中引用多个用户。例如,您可以创建一个名为MANAGERS的用户别名,其中包括usersmurfbob,如下所示:

User_Alias MANAGERS = smurf,bob

您可以使用命令别名将多个命令分组在一起。例如,您可以创建一个名为USER_CMDS的命令别名,其中包括useradduserdelusermod命令:

Cmnd_Alias USER_CMDS = /usr/sbin/useradd, /usr/sbin/userdel, /usr/sbin/usermod

现在您可以同时使用别名:

MANAGERS ALL=(ALL) USER_CMDS

授予用户smurfbob运行useradduserdelusermod命令的权限。

组权限

您还可以在/etc/sudoers文件中指定组。组名前面加上百分号字符,如下所示:

%group hosts=(user:group) commands

以下规则将授予developers组在系统上安装任何软件包的权限:

%developers ALL=(ALL) NOPASSWD: /usr/bin/apt-get install

以下规则将授予developers组在系统上运行任何命令的权限:

%developers ALL=(ALL) NOPASSWD: ALL

列出用户权限

您可以使用命令sudo -lU来显示用户可以运行的sudo命令列表:

sudo -lU username 

例如,您可以运行以下命令:

root@ubuntu-linux:~# sudo -lU smurf
Matching Defaults entries for smurf on ubuntu-linux: 
 env_reset, mail_badpass

User smurf may run the following commands on ubuntu-linux:
 (ALL) NOPASSWD: /usr/sbin/useradd, /usr/bin/apt-get install terminator

列出用户smurf可以运行的所有sudo命令。

如果用户不被允许运行任何sudo命令,则sudo-lU命令的输出将如下所示:

root@ubuntu-linux:~# sudo -lU rachel
User rachel is not allowed to run sudo on ubuntu-linux.

visudo 与/etc/sudoers

您可能已经注意到,我使用visudo命令编辑文件/etc/sudoers,您可能会问自己一个非常合理的问题:为什么不直接编辑文件/etc/sudoers而不使用visudo?好吧,我将以实际的方式回答您的问题。

首先,运行visudo命令并添加以下行:

THISLINE=WRONG

现在尝试保存并退出:

root@ubuntu-linux:~# visudo
>>> /etc/sudoers: syntax error near line 14 <<< 
What now?
Options are:
 (e)dit sudoers file again
 e(x)it without saving changes to sudoers file 
 (Q)uit and save changes to sudoers file (DANGER!)
What now?

正如您所看到的,visudo命令检测到错误,并指定了错误发生的行号。

为什么这很重要?好吧,如果您保存了带有错误的文件,/etc/sudoers中的所有sudo规则都将无法工作!让我们按Q保存更改,然后尝试列出用户smurf可以运行的sudo命令:

What now? Q
root@ubuntu-linux:~# sudo -lU smurf
>>> /etc/sudoers: syntax error near line 14 <<< 
sudo: parse error in /etc/sudoers near line 14 
sudo: no valid sudoers sources found, quitting 
sudo: unable to initialize policy plugin

我们遇到了一个错误,所有的sudo规则现在都被破坏了!返回并运行visudo命令,删除包含错误的行。

如果您直接编辑文件/etc/sudoers而不使用visudo命令,它将不会检查语法错误,这可能会导致灾难性后果,就像您看到的那样。因此,这里的经验法则是:在编辑/etc/sudoers文件时始终使用visudo

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 添加一个sudo规则,使用户smurf可以运行fdisk命令。

  2. 添加一个sudo规则,使developers组可以运行apt-get命令。

  3. 列出用户smurf的所有sudo命令。

网络出了什么问题?

当网络出现问题时,我们都会感到愤怒。没有连接到互联网,这个世界就没有乐趣。在本章中,你将学习 Linux 网络的基础知识。你还将学习如何检查两个主机之间的网络连接,并获得对 DNS 工作原理的实际理解,以及更多!

第十六章:测试网络连接

在 Linux 机器上检查是否有互联网访问的简单方法是尝试连接互联网上的任何远程主机(服务器)。这可以通过使用ping命令来完成。一般来说,ping命令的语法如下:

ping [options] host

例如,要测试你是否能够到达google.com,你可以运行以下命令:

root@ubuntu-linux:~# ping google.com
PING google.com (172.217.1.14) 56(84) bytes of data.
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=1 ttl=55 time=38.7 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=2 ttl=55 time=38.7 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=3 ttl=55 time=40.4 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=4 ttl=55 time=36.6 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=5 ttl=55 time=40.8 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=6 ttl=55 time=38.6 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=7 ttl=55 time=38.9 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=8 ttl=55 time=37.1 ms
^C
--- google.com ping statistics ---
8 packets transmitted, 8 received, 0% packet loss, time 66ms 
rtt min/avg/max/mdev = 36.555/38.724/40.821/1.344 ms

ping命令发送一个叫做ICMP 回显请求的数据包(数据单位)到指定的主机,并等待主机发送一个叫做ICMP 回显回复的数据包来确认它确实收到了初始数据包。如果主机像我们在例子中看到的那样回复,那么就证明我们能够到达主机。这就像你给朋友家寄一个包裹,等待朋友发短信确认收到一样。

请注意,没有任何选项,ping命令会持续发送数据包,直到你按下Ctrl + C

你可以使用-c选项来指定你想发送到主机的数据包数量。例如,只向google.com发送三个数据包,你可以运行以下命令:

root@ubuntu-linux:~# ping -c 3 google.com
PING google.com (172.217.1.14) 56(84) bytes of data.

64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=1 ttl=55 time=39.3 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=2 ttl=55 time=49.7 ms 
64 bytes from iad23s25-in-f14.1e100.net (172.217.1.14): icmp_seq=3 ttl=55 time=40.8 ms

--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 59ms rtt min/avg/max/mdev = 39.323/43.267/49.708/4.595 ms

如果你没有连接到互联网,你将从ping命令得到以下输出:

root@ubuntu-linux:~# ping google.com
ping: google.com: Name or service not known

列出你的网络接口

你可以通过查看/sys/class/net目录的内容来列出系统上可用的网络接口:

root@ubuntu-linux:~# ls /sys/class/net 
eth0 lo wlan0

我的系统上有三个网络接口:

  1. eth0:以太网接口

  2. lo:回环接口

  3. wlan0:Wi-Fi 接口

请注意,根据你的计算机硬件,你可能会得到不同的网络接口名称。

ip 命令

你也可以使用ip link show命令查看系统上可用的网络接口:

root@ubuntu-linux:~# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
 link/ether f0:de:f1:d3:e1:e1 brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
 link/ether 10:0b:a9:6c:89:a0 brd ff:ff:ff:ff:ff:ff

nmcli 命令

我更喜欢的另一种方法是使用nmcli设备状态命令:

root@ubuntu-linux:~# nmcli device status 
DEVICE TYPE STATE CONNECTION
wlan0 wifi      connected   SASKTEL0206-5G 
eth0  ethernet  unavailable --
lo    loopback  unmanaged   --

你可以从输出中看到每个网络接口的连接状态。我目前是通过我的 Wi-Fi 接口连接到互联网的。

检查你的 IP 地址

没有手机号码,你就不能给朋友打电话;同样,你的计算机需要一个 IP 地址才能连接到互联网。你可以使用许多不同的方法来检查你的机器的 IP 地址。你可以使用老式(但仍然流行的)ifconfig命令,后面跟着连接到互联网的网络接口的名称:

root@ubuntu-linux:~# ifconfig wlan0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
 inet 172.16.1.73 netmask 255.255.255.0 broadcast 172.16.1.255 
       inet6 fe80::3101:321b:5ec3:cf9 prefixlen 64 scopeid 0x20<link> 
       ether 10:0b:a9:6c:89:a0 txqueuelen 1000 (Ethernet)
 RX packets 265 bytes 27284 (26.6 KiB)
 RX errors 0 dropped 0 overruns 0 frame 0
 TX packets 165 bytes 28916 (28.2 KiB)
 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

你也可以使用-a选项列出所有网络接口:

root@ubuntu-linux:~# ifconfig -a
eth0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
 ether f0:de:f1:d3:e1:e1 txqueuelen 1000 (Ethernet) 
      RX packets 0 bytes 0 (0.0 B)
 RX errors 0 dropped 0 overruns 0 frame 0
 TX packets 0 bytes 0 (0.0 B)
 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 
      device interrupt 20 memory 0xf2500000-f2520000

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
 inet 127.0.0.1 netmask 255.0.0.0
 inet6 ::1 prefixlen 128 scopeid 0x10<host>
 loop txqueuelen 1000 (Local Loopback) 
     RX packets 4 bytes 156 (156.0 B)
 RX errors 0 dropped 0 overruns 0 frame 0
 TX packets 4 bytes 156 (156.0 B)
 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
 inet 172.16.1.73 netmask 255.255.255.0 broadcast 172.16.1.255 
     inet6 fe80::3101:321b:5ec3:cf9 prefixlen 64 scopeid 0x20<link> 
     ether 10:0b:a9:6c:89:a0 txqueuelen 1000 (Ethernet)
 RX packets 482 bytes 45500 (44.4 KiB)
 RX errors 0 dropped 0 overruns 0 frame 0
 TX packets 299 bytes 57788 (56.4 KiB)
 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

你可以从输出中看到,我只通过我的 Wi-Fi 接口(wlan0)连接到互联网,我的 IP 地址是172.16.1.73

回环是什么?

回环(或lo)是计算机用来与自身通信的虚拟接口;它主要用于故障排除。回环接口的 IP 地址是127.0.0.1,如果你想 ping 自己!尽管 ping 127.0.0.1

你也可以使用更新的ip命令来检查你的机器的 IP 地址。例如,你可以运行ip address show命令来列出并显示所有的网络接口的状态:

root@ubuntu-linux:~# ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
 inet 127.0.0.1/8 scope host lo
 valid_lft forever preferred_lft forever 
    inet6 ::1/128 scope host
 valid_lft forever preferred_lft forever
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state 
        DOWN link/ether f0:de:f1:d3:e1:e1 brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state 
    UP link/ether 10:0b:a9:6c:89:a0 brd ff:ff:ff:ff:ff:ff
 inet 172.16.1.73/24 brd 172.16.1.255 scope global dynamic 
      noprefixroute wlan0 valid_lft 85684sec preferred_lft 85684sec
 inet6 fe80::3101:321b:5ec3:cf9/64 scope link noprefixroute 
      valid_lft forever preferred_lft forever

检查你的网关地址

你的计算机从路由器那里获取了一个 IP 地址;这个路由器也被称为默认网关,因为它将你连接到外部世界(互联网)。这些路由器随处可见;它们在你家、咖啡店、学校、医院等等。

你可以通过运行以下任一命令来检查你的默认网关的 IP 地址:

  • route -n

  • netstat -rn

  • ip route

让我们从第一个命令route -n开始:

root@ubuntu-linux:~# route -n Kernel IP routing table
Destination Gateway       Genmask       Flags  Metric Ref Use Iface
0.0.0.0     172.16.1.254  0.0.0.0       UG     600     0   0 wlan0
172.16.1.0  0.0.0.0       255.255.255.0 U      600     0   0 wlan0

您可以从输出中看到我的默认网关 IP 地址为172.16.1.254。现在让我们尝试第二个命令netstat -rn

root@ubuntu-linux:~# netstat -rn 
Kernel IP routing table
Destination   Gateway      Genmask       Flags  MSS Window irtt Iface
0.0.0.0       172.16.1.254 0.0.0.0       UG     0   0      0    wlan0
172.16.1.0    0.0.0.0      255.255.255.0 U      0   0      0    wlan0

输出几乎看起来相同。现在输出与第三个命令ip route有一点不同:

root@ubuntu-linux:~# ip route
default via 172.16.1.254 dev wlan0 proto dhcp metric 600
172.16.1.0/24 dev wlan0 proto kernel scope link src 172.16.1.73 metric 600

默认网关 IP 地址显示在第一行:默认通过172.16.1.254。您还应该能够 ping 默认网关:

root@ubuntu-linux:~# ping -c 2 172.16.1.254
PING 172.16.1.254 (172.16.1.254) 56(84) bytes of data.
64 bytes from 172.16.1.254: icmp_seq=1 ttl=64 time=1.38 ms
64 bytes from 172.16.1.254: icmp_seq=2 ttl=64 time=1.62 ms

--- 172.16.1.254 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms rtt min/avg/max/mdev = 1.379/1.501/1.624/0.128 ms

使用 traceroute 飞行

您现在已经准备好离开家去上班了。您必须通过不同的街道最终到达目的地,对吧?嗯,这与您尝试在互联网上到达主机(网站)时非常相似;您采取的路线从默认网关开始,以目的地结束。

您可以使用traceroute命令跟踪到任何目的地的路由。traceroute命令的一般语法如下:

traceroute destination

例如,您可以通过运行以下命令跟踪从您的计算机到google.com的路由:

root@ubuntu-linux:~# traceroute google.com
traceroute to google.com (172.217.1.14), 30 hops max, 60 byte packets
 1 172.16.1.254 (172.16.1.254) 15.180 ms 15.187 ms 15.169 ms
 2 207-47-195-169.ngai.static.sasknet.sk.ca (207.47.195.169) 24.059 ms
 3 142.165.0.110 (142.165.0.110) 50.060 ms 54.305 ms 54.903 ms
 4 72.14.203.189 (72.14.203.189) 53.720 ms 53.997 ms 53.948 ms
 5 108.170.250.241 (108.170.250.241) 54.185 ms 35.506 ms 108.170.250.225
 6 216.239.35.233 (216.239.35.233) 37.005 ms 35.729 ms 38.655 ms
 7 yyz10s14-in-f14.1e100.net (172.217.1.14) 41.739 ms 41.667 ms 41.581 ms

正如您所看到的,我的机器花了七次旅行(跳跃)才到达我的最终目的地google.com。请注意,第一跳是我的默认网关,最后一跳是目的地。

当您在解决连接问题时,traceroute命令非常有用。例如,要到达特定目的地可能需要很长时间;在这种情况下,traceroute可以帮助您检测到达目的地路径上的任何故障点。

破坏您的 DNS

互联网上的每个网站(目的地)都必须有一个 IP 地址。然而,我们人类对数字不太擅长,所以我们发明了域名系统DNS)。DNS 的主要功能是将名称(域名)与 IP 地址关联起来;这样,我们在浏览互联网时就不需要记住 IP 地址了...感谢上帝的 DNS!

每次您在浏览器上输入域名时,DNS 都会将(解析)域名转换为其相应的 IP 地址。您的 DNS 服务器的 IP 地址存储在文件/etc/resolv.conf中:

root@ubuntu-linux:~# cat /etc/resolv.conf 
# Generated by NetworkManager
nameserver 142.165.200.5

我正在使用由我的互联网服务提供商ISP)提供的 DNS 服务器142.165.200.5。您可以使用nslookup命令来查看 DNS 的工作情况。nslookup命令的一般语法如下:

nslookup domain_name

nslookup命令使用 DNS 获取域名的 IP 地址。例如,要获取facebook.com的 IP 地址,您可以运行以下命令:

root@ubuntu-linux:~# nslookup facebook.com 
Server: 142.165.200.5
Address: 142.165.200.5#53

Non-authoritative answer:
Name: facebook.com 
Address: 157.240.3.35 
Name: facebook.com
Address: 2a03:2880:f101:83:face:b00c:0:25de

请注意,它在输出的第一行显示了我的 DNS 服务器的 IP 地址。您还可以看到facebook.com的 IP 地址157.240.3.35

您还可以 pingfacebook.com

root@ubuntu-linux:~# ping -c 2 facebook.com
PING facebook.com (157.240.3.35) 56(84) bytes of data.
64 bytes from edge-star-mini-shv-01-sea1.facebook.com (157.240.3.35): 
icmp_seq=1 ttl=55 time=34.6 ms
64 bytes from edge-star-mini-shv-01-sea1.facebook.com (157.240.3.35): 
icmp_seq=2 ttl=55 time=33.3 ms

--- facebook.com ping statistics ---

2 packets transmitted, 2 received, 0% packet loss, time 2ms 
rtt min/avg/max/mdev = 33.316/33.963/34.611/0.673 ms

现在让我们破坏一切!我妈妈曾经告诉我,我必须破坏一切,这样我才能理解它们是如何工作的。让我们看看没有 DNS 的生活是什么样子,通过清空文件/etc/resolv.conf

root@ubuntu-linux:~# echo > /etc/resolv.conf 
root@ubuntu-linux:~# cat /etc/resolv.conf

root@ubuntu-linux:~#

现在让我们对facebook.com进行nslookup

root@ubuntu-linux:~# nslookup facebook.com

您会看到它挂起,因为它无法再解析域名。现在让我们尝试 pingfacebook.com

root@ubuntu-linux:~# ping facebook.com
ping: facebook.com: Temporary failure in name resolution

您会收到错误消息名称解析临时失败,这是说您的 DNS 出了问题的一种花哨方式!但是,您仍然可以通过使用其 IP 地址来 pingfacebook.com

root@ubuntu-linux:~# ping -c 2 157.240.3.35
PING 157.240.3.35 (157.240.3.35) 56(84) bytes of data.
64 bytes from 157.240.3.35: icmp_seq=1 ttl=55 time=134 ms
64 bytes from 157.240.3.35: icmp_seq=2 ttl=55 time=34.4 ms

--- 157.240.3.35 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms 
rtt min/avg/max/mdev = 34.429/84.150/133.872/49.722 ms

让我们修复我们的 DNS,但这次我们将不使用我们的 ISP 的 DNS 服务器;相反,我们将使用 Google 的公共 DNS 服务器8.8.8.8

root@ubuntu-linux:~# echo "nameserver 8.8.8.8" > /etc/resolv.conf 
root@ubuntu-linux:~# cat /etc/resolv.conf
nameserver 8.8.8.8

现在让我们再次对facebook.com进行nslookup

root@ubuntu-linux:~# nslookup facebook.com Server: 8.8.8.8
Address: 8.8.8.8#53

Non-authoritative answer:
Name: facebook.com 
Address: 31.13.80.36 
Name: facebook.com
Address: 2a03:2880:f10e:83:face:b00c:0:25de

请注意,我的活动 DNS 现在已更改为8.8.8.8。我还得到了facebook.com的不同 IP 地址,这是因为 Facebook 在世界各地的许多不同服务器上运行。

更改您的主机名

每个网站都有一个在互联网上唯一标识它的域名;同样,计算机有一个在网络上唯一标识它的主机名。

您计算机的主机名存储在文件/etc/hostname中:

root@ubuntu-linux:~# cat /etc/hostname 
ubuntu-linux

您可以使用主机名来访问同一网络(子网)中的其他计算机。例如,我有另一台计算机,主机名为backdoor,目前正在运行,我可以 ping 它:

root@ubuntu-linux:~# ping backdoor
PING backdoor (172.16.1.67) 56(84) bytes of data.
64 bytes from 172.16.1.67 (172.16.1.67): icmp_seq=1 ttl=64 time=3.27 ms
64 bytes from 172.16.1.67 (172.16.1.67): icmp_seq=2 ttl=64 time=29.3 ms
64 bytes from 172.16.1.67 (172.16.1.67): icmp_seq=3 ttl=64 time=51.4 ms
^C
--- backdoor ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 20ms 
rtt min/avg/max/mdev = 3.272/27.992/51.378/19.662 ms

请注意,backdoor位于相同的网络(子网)并且具有 IP 地址172.16.1.67。我也可以 ping 自己:

root@ubuntu-linux:~# ping ubuntu-linux
PING ubuntu-linux (172.16.1.73) 56(84) bytes of data.
64 bytes from 172.16.1.73 (172.16.1.73): icmp_seq=1 ttl=64 time=0.025 ms
64 bytes from 172.16.1.73 (172.16.1.73): icmp_seq=2 ttl=64 time=0.063 ms
^C
--- ubuntu-linux ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 14ms 
rtt min/avg/max/mdev = 0.025/0.044/0.063/0.019 ms

这是一种聪明的方法来显示您计算机的 IP 地址-简单地 ping 自己!

您可以使用hostnamectl命令来查看和设置计算机的主机名:

root@ubuntu-linux:~# hostnamectl 
    Static hostname: ubuntu-linux
 Icon name: computer-vm 
            Chassis: vm
 Machine ID: 106fd80252e541faafa4e54a250d1216 
            Boot ID: c5508514af114b4b80c55d4267c25dd4
 Virtualization: oracle
 Operating System: Ubuntu 18.04.3 LTS 
             Kernel: Linux 4.15.0-66-generic
 Architecture: x86-64

要更改计算机的主机名,您可以使用hostnamectl set-hostname命令,然后跟上新的主机名:

hostnamectl set-hostname new_hostname

例如,您可以通过运行以下命令将计算机的主机名更改为myserver

root@ubuntu-linux:~# hostnamectl set-hostname myserver 
root@ubuntu-linux:~# su -
root@myserver:~#

请记住,您需要打开一个新的 shell 会话,以便您的 shell 提示显示新的主机名。您还可以看到文件/etc/hostname已更新,因为它包含新的主机名:

root@ubuntu-linux:~# cat /etc/hostname 
myserver

重新启动您的网络接口

这可能是一种被滥用的方法,但有时重新启动是许多与计算机相关的问题的最快解决方法!我自己也常常滥用重新启动解决大部分计算机问题。

您可以使用ifconfig命令将网络接口关闭;您必须在网络接口名称后面跟随down标志,如下所示:

ifconfig interface_name down

例如,我可以通过运行以下命令关闭我的 Wi-Fi 接口wlan0

root@myserver:~# ifconfig wlan0 down

您可以使用up标志来启用网络接口:

ifconfig interface_name up

例如,我可以通过运行以下命令重新启动我的 Wi-Fi 接口:

root@myserver:~# ifconfig wlan0 up

您可能还希望同时重新启动所有网络接口。这可以通过以下方式重新启动NetworkManager服务来完成:

root@myserver:~# systemctl restart NetworkManager

现在是时候通过一个可爱的知识检查练习来测试您对 Linux 网络的理解了。

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 将您的主机名更改为darkarmy

  2. 显示您的默认网关的 IP 地址。

  3. 从您的计算机到www.ubuntu.com的路由跟踪。

  4. 显示您的 DNS 的 IP 地址。

  5. 显示www.distrowatch.com的 IP 地址。

  6. 关闭您的以太网接口。

  7. 重新启动您的以太网接口。

Bash 脚本很有趣

在 Linux 中完成特定任务时,你经常会发现自己一遍又一遍地运行相同的一组命令。这个过程会浪费很多宝贵的时间。在本章中,你将学习如何创建 bash 脚本,以便在 Linux 中更加高效。

第十七章:创建简单脚本

我们的第一个 bash 脚本将是一个简单的脚本,它将在屏幕上输出一行“你好,朋友!”。在艾略特的主目录中,创建一个名为hello.sh的文件,并插入以下两行:

elliot@ubuntu-linux:~$ cat hello.sh 
#!/bin/bash
echo "Hello Friend!"

现在我们需要将脚本设置为可执行:

elliot@ubuntu-linux:~$ chmod a+x hello.sh

最后,运行脚本:

elliot@ubuntu-linux:~$ ./hello.sh 
Hello Friend!

恭喜!你现在已经创建了你的第一个 bash 脚本!让我们花一分钟时间讨论一些事情;每个 bash 脚本必须做到以下几点:

  • #!/bin/bash

  • 要可执行

你必须在任何 bash 脚本的第一行插入#!/bin/bash;字符序列#!被称为 shebang 或 hashbang,后面跟着 bash shell 的路径。

PATH 变量

你可能已经注意到我使用了./hello.sh来运行脚本;如果省略前导的./,你会得到一个错误:

elliot@ubuntu-linux:~$ hello.sh 
hello.sh: command not found

shell 找不到命令hello.sh。当你在终端上运行一个命令时,shell 会在存储在PATH变量中的一组目录中寻找该命令。

你可以使用echo命令查看你的PATH变量的内容:

elliot@ubuntu-linux:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

冒号字符分隔每个目录的路径。你不需要包括这些目录中任何命令或脚本(或任何可执行文件)的完整路径。到目前为止,你学到的所有命令都驻留在/bin/sbin中,它们都存储在你的PATH变量中。因此,你可以运行pwd命令:

elliot@ubuntu-linux:~$ pwd
/home/elliot

没有必要包含它的完整路径:

elliot@ubuntu-linux:~$ /bin/pwd
/home/elliot

好消息是你可以很容易地将一个目录添加到你的PATH变量中。例如,要将/home/elliot添加到你的PATH变量中,你可以使用export命令如下:

elliot@ubuntu-linux:~$ export PATH=$PATH:/home/elliot

现在你不需要前导的./来运行hello.sh脚本:

elliot@ubuntu-linux:~$ hello.sh 
Hello Friend!

它将运行,因为 shell 现在也在/home/elliot目录中寻找可执行文件:

elliot@ubuntu-linux:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/elliot

好了!现在让我们创建几个更多的 bash 脚本。我们将创建一个名为hello2.sh的脚本,它打印出“你好,朋友!”然后显示你当前的工作目录:

elliot@ubuntu-linux:~$ cat hello2.sh 
#!/bin/bash
echo "Hello Friend!" 
pwd

现在让我们运行它:

elliot@ubuntu-linux:~$ hello2.sh
-bash: /home/elliot/hello2.sh: Permission denied

糟糕!我忘记了要将其设置为可执行:

elliot@ubuntu-linux:~$ chmod a+x hello2.sh 
elliot@ubuntu-linux:~$ ./hello2.sh
Hello Friend!
/home/elliot

读取用户输入

让我们创建我们的hello.sh脚本的更好版本。我们将让用户输入他/她的名字,然后我们将向用户打招呼;创建一个名为greet.sh的脚本,包含以下几行:

elliot@ubuntu-linux:~$ cat greet.sh 
#!/bin/bash
echo "Please enter your name:" 
read name
echo "Hello $name!"

现在让脚本可执行,然后运行它:

elliot@ubuntu-linux:~$ chmod a+x greet.sh 
elliot@ubuntu-linux:~$ ./greet.sh
Please enter your name:

当你运行脚本时,它会提示你输入你的名字;我输入了Elliot作为我的名字:

elliot@ubuntu-linux:~$ ./greet.sh 
Please enter your name:
Elliot
Hello Elliot!

脚本向我打招呼说“你好,艾略特!”。我们使用read命令获取用户输入,并注意在echo语句中,我们使用了美元符号$来打印变量name的值。

让我们创建另一个脚本,从用户那里读取文件名,然后输出文件的大小(以字节为单位);我们将命名我们的脚本为size.sh

elliot@ubuntu-linux:~$ cat size.sh
#!/bin/bash
echo "Please enter a file path:" 
read file
filesize=$(du -bs $file| cut -f1)
echo "The file size is $filesize bytes"

并且永远不要忘记将脚本设置为可执行:

elliot@ubuntu-linux:~$ chmod a+x size.sh

现在让我们运行脚本:

elliot@ubuntu-linux:~$ size.sh 
Please enter a file path
/home/elliot/size.sh
The file size is 128 bytes

我使用size.sh作为文件路径,输出为 128 字节;是真的吗?让我们来检查一下:

elliot@ubuntu-linux:~$ du -bs size.sh
128 size.sh

的确如此;请注意脚本中的以下一行:

filesize=$(du -bs $file| cut -f1)

它将du -bs $file | cut -f1命令的结果存储在变量filesize中:

elliot@ubuntu-linux:~$ du -bs size.sh | cut -f1 
128

还要注意,命令du -bs $file cut -f1被括号和美元符号(在左边)包围;这被称为命令替换。一般来说,命令替换的语法如下:

var=$(command)

command的结果将存储在变量var中。

向脚本传递参数

除了从用户那里读取输入,你还可以将参数传递给 bash 脚本。例如,让我们创建一个名为size2.sh的 bash 脚本,它做的事情与脚本size.sh相同,但是不是从用户那里读取文件,而是将其作为参数传递给脚本size2.sh

elliot@ubuntu-linux:~$ cat size2.sh 
#!/bin/bash
filesize=$(du -bs $1| cut -f1)
echo "The file size is $filesize bytes"

现在让我们将脚本设置为可执行:

elliot@ubuntu-linux:~$ chmod a+x size2.sh

最后,你可以运行脚本:

elliot@ubuntu-linux:~$ size2.sh /home/elliot/size.sh 
The file size is 128 bytes

你将得到与size.sh相同的输出。注意我们提供了文件路径

/home/elliot/size.sh作为脚本size2.sh的参数。

我们在脚本size2.sh中只使用了一个参数,它由$1引用。你也可以传递多个参数;让我们创建另一个脚本size3.sh,它接受两个文件(两个参数)并输出每个文件的大小:

elliot@ubuntu-linux:~$ cat size3.sh #!/bin/bash
filesize1=$(du -bs $1| cut -f1) 
filesize2=$(du -bs $2| cut -f1) 
echo "$1 is $filesize1 bytes" 
echo "$2 is $filesize2 bytes"

现在使脚本可执行并运行它:

elliot@ubuntu-linux:~$ size3.sh /home/elliot/size.sh /home/elliot/size3.sh
/home/elliot/size.sh is 128 bytes
/home/elliot/size3.sh is 136 bytes

太棒了!如你所见,第一个参数由$1引用,第二个参数由$2引用。所以一般来说:

bash_script.sh argument1 argument2 argument3 ...
 $1         $2         $3

使用 if 条件

你可以通过使其在不同的情况下表现不同来为你的 bash 脚本增加智能。为此,我们使用条件if语句。

一般来说,if 条件的语法如下:

if [ condition is true ]; then 
    do this ...
fi

例如,让我们创建一个名为empty.sh的脚本,它将检查文件是否为空:

elliot@ubuntu-linux:~$ cat empty.sh 
#!/bin/bash
filesize=$(du -bs $1 | cut -f1) 
if [ $filesize -eq 0 ]; then 
echo "$1 is empty!"
fi

现在让我们使脚本可执行,并创建一个名为zero.txt的空文件:

elliot@ubuntu-linux:~$ chmod a+x empty.sh 
elliot@ubuntu-linux:~$ touch zero.txt

现在让我们在文件zero.txt上运行脚本:

elliot@ubuntu-linux:~$ ./empty.sh zero.txt 
zero.txt is empty!

如你所见,脚本正确地检测到zero.txt是一个空文件;这是因为在这种情况下测试条件为真,因为文件zero.txt的确是零字节大小的:

if [ $filesize -eq 0 ];

我们使用了-eq来测试相等。现在如果你在一个非空文件上运行脚本,将不会有输出:

elliot@ubuntu-linux:~$ ./empty.sh size.sh 
elliot@ubuntu-linux:~$

我们需要修改脚本empty.sh,以便在传递非空文件时显示输出;为此,我们将使用if-else语句:

if [ condition is true ]; then 
    do this ...
else
    do this instead ...
fi

让我们通过添加以下else语句来编辑empty.sh脚本:

elliot@ubuntu-linux:~$ cat empty.sh 
#!/bin/bash
filesize=$(du -bs $1 | cut -f1) 
if [ $filesize -eq 0 ]; then 
echo "$1 is empty!"
else
echo "$1 is not empty!" 
fi

现在让我们重新运行脚本:

elliot@ubuntu-linux:~$ ./empty.sh size.sh 
size.sh is not empty!
elliot@ubuntu-linux:~$ ./empty.sh zero.txt 
zero.txt is empty!

如你所见,现在它完美地运行了!

你也可以使用elifelse-if)语句来创建多个测试条件:

if [ condition is true ]; then 
    do this ...
elif [ condition is true]; then 
    do this instead ...
fi

让我们创建一个名为filetype.sh的脚本,它检测文件类型。脚本将输出文件是普通文件、软链接还是目录:

elliot@ubuntu-linux:~$ cat filetype.sh 
#!/bin/bash
file=$1
if [ -f $1 ]; then
echo "$1 is a regular file" 
elif [ -L $1 ]; then
echo "$1 is a soft link" 
elif [ -d $1 ]; then 
echo "$1 is a directory" 
fi

现在让我们使脚本可执行,并创建一个指向/tmp的软链接,名为tempfiles

elliot@ubuntu-linux:~$ chmod a+x filetype.sh 
elliot@ubuntu-linux:~$ ln -s /tmp tempfiles

现在在任何目录上运行脚本:

elliot@ubuntu-linux:~$ ./filetype.sh /bin
/bin is a directory

它正确地检测到/bin是一个目录。现在在任何普通文件上运行脚本:

elliot@ubuntu-linux:~$ ./filetype.sh zero.txt 
zero.txt is a regular file

它正确地检测到zero.txt是一个普通文件。最后,在任何软链接上运行脚本:

elliot@ubuntu-linux:~$ ./filetype.sh tempfiles 
tempfiles is a soft link

它正确地检测到tempfiles是一个软链接。

以下man页面包含了所有的测试条件:

elliot@ubuntu-linux:~$ man test

所以永远不要死记硬背!利用并使用 man 页面。

在 bash 脚本中循环

循环的能力是 bash 脚本的一个非常强大的特性。例如,假设你想要在终端上打印出"Hello world"这一行 20 次;一个天真的方法是创建一个有 20 个echo语句的脚本。幸运的是,循环提供了一个更聪明的解决方案。

使用 for 循环

for循环有几种不同的语法。如果你熟悉 C++或 C 编程,那么你会认出以下for循环的语法:

for ((initialize ; condition ; increment)); do
// do something 
done

使用前面提到的 C 风格语法;以下for循环将打印出"Hello World"二十次:

for ((i = 0 ; i < 20 ; i++)); do 
    echo "Hello World"
done

循环将整数变量i初始化为0,然后测试条件(i < 20);如果为真,则执行 echo "Hello World"这一行,并递增变量i一次,然后循环再次运行,直到i不再小于20

现在让我们创建一个名为hello20.sh的脚本,其中包含我们刚讨论的for循环:

elliot@ubuntu-linux:~$ cat hello20.sh 
#!/bin/bash
for ((i = 0 ; i < 20 ; i++)); do 
 echo "Hello World"
done

现在使脚本可执行并运行它:

elliot@ubuntu-linux:~$ chmod a+x hello20.sh 
elliot@ubuntu-linux:~$ hello20.sh
Hello World 
Hello World
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Hello World

它输出了"Hello World"这一行二十次,正如我们所预期的那样。除了 C 风格的语法,你也可以在for循环中使用范围语法:

for i in {1..20}; do 
    echo "Hello World"
done

这也将输出"Hello World"20 次。这种范围语法在处理文件列表时特别有用。为了演示,创建以下五个文件:

elliot@ubuntu-linux:~$ touch one.doc two.doc three.doc four.doc five.doc

现在假设我们想要将所有五个文件的扩展名从.doc改为

.document。我们可以创建一个名为rename.sh的脚本,其中包含以下for循环:

#!/bin/bash
for i in /home/elliot/*.doc; do
    mv $i $(echo $i | cut -d. -f1).document
done

使脚本可执行并运行它:

#!/bin/bash
elliot@ubuntu-linux:~$ chmod a+x rename.sh 
elliot@ubuntu-linux:~$ ./rename.sh 
elliot@ubuntu-linux:~$ ls *.document
five.document four.document one.document three.document two.document

正如你所看到的,它将所有扩展名为.doc的文件重命名为.document。现在想象一下,如果你想对一百万个文件执行此操作。如果你不懂 bash 脚本,你可能要花十年的时间。我们都应该感谢 Linux 之神的 bash 脚本。

使用while循环

while循环是另一个流行且直观的循环。while循环的一般语法如下:

while [ condition is true ]; do
  // do something 
done

例如,我们可以创建一个简单的脚本numbers.sh,打印从一到十的数字:

elliot@ubuntu-linux:~$ cat numbers.sh 
#!/bin/bash
number=1
while [ $number -le 10 ]; do 
echo $number 
number=$(($number+1))
done

使脚本可执行并运行它:

elliot@ubuntu-linux:~$ chmod a+x numbers.sh 
elliot@ubuntu-linux:~$ ./numbers.sh
1
2
3
4
5
6
7
8
9
10

脚本很容易理解;我们首先将变量 number 初始化为1

number=1

然后我们创建了一个测试条件,只要变量number小于或等于 10,while循环将继续运行:

while [ $number -le 10 ]; do

while循环的主体中,我们首先打印变量number的值,然后将其增加 1。请注意,要评估算术表达式,它需要在双括号内,如$((arithmetic-expression))

echo $number 
number=$(($number+1))

现在是时候玩一些有趣的东西了!我们将创建一个猜数字游戏。但在我们开始之前,让我向你介绍一个非常酷的命令。你可以使用shuf命令来生成随机排列。例如,要生成 1 到 10 之间数字的随机排列,你可以运行以下命令:

elliot@ubuntu-linux:~$ shuf -i 1-10 
1
6
5
2
10
8
3
9
7
4

请记住,我的输出很可能与你的输出不同,因为它是随机的!你有一百万分之一的机会和我有相同的输出。

现在我们可以使用-n选项从排列中选择一个数字。这个数字也将是随机的。因此,要生成 1 到 10 之间的随机数,你可以运行以下命令:

elliot@ubuntu-linux:~$ shuf -i 1-10 -n 1
6

输出将是 1 到 10 之间的一个随机数。shuf命令将在我们的游戏中发挥关键作用。我们将生成 1 到 10 之间的随机数,然后我们将看看用户(玩家)猜中随机数需要多少次尝试。

这是我们精心制作的脚本game.sh

elliot@ubuntu-linux:~$ cat game.sh 
#!/bin/bash
random=$(shuf -i 1-10 -n 1) #generate a random number between 1 and 10\. 
echo "Welcome to the Number Guessing Game"
echo "The lucky number is between 1 and 10." 
echo "Can you guess it?"
tries=1
while [ true ]; do
echo -n "Enter a Number between 1-10: " 
read number
if [ $number -gt $random ]; then 
echo "Too high!"
elif [ $number -lt $random ]; then 
echo "Too low!"
else
echo "Correct! You got it in $tries tries" 
break #exit the loop
fi 
tries=$(($tries+1)) 
done

现在使脚本可执行并运行它来开始游戏:

elliot@ubuntu-linux:~$ chmod a+x game.sh 
elliot@ubuntu-linux:~$ game.sh
Welcome to the Number Guessing Game 
The lucky number is between 1 and 10\. 
Can you guess it?
Enter a Number between 1-10: 4 
Too low!
Enter a Number between 1-10: 7 
Too low!
Enter a Number between 1-10: 9 
Too high!
Enter a Number between 1-10: 8 
Correct! You got it in 4 tries

在我的第一次尝试游戏中,我猜了四次;我打赌你可以轻松地击败我!

让我们逐行查看我们的游戏脚本。我们首先生成一个 1 到 10 之间的随机数,并将其赋值给变量random

random=$(shuf -i 1-10 -n 1) #generate a random number between 1 and 10.

请注意,你可以在你的 bash 脚本中添加注释,就像我在这里使用井号字符,后面跟上你的注释一样。

然后我们打印三行来向玩家解释游戏规则:

echo "Welcome to the Number Guessing Game" 
echo "The lucky number is between 1 and 10." 
echo "Can you guess it?"

接下来,我们将变量tries初始化为1,以便我们可以跟踪玩家猜了多少次:

tries=1

然后我们进入游戏循环:

while [ true ]; do

请注意,测试条件while [ true ]将始终为true,因此循环将永远运行(无限循环)。

游戏循环中我们做的第一件事是要求玩家输入 1 到 10 之间的数字:

echo -n "Enter a Number between 1-10: " 
read number

然后我们测试玩家输入的数字是大于、小于还是等于random数字:

if [ $number -gt $random ]; then 
echo "Too high!"
elif [ $number -lt $random ]; then 
echo "Too low!"
else
echo "Correct! You got it in $tries tries" 
break #exit the loop
fi

如果number大于random,我们告诉玩家猜测太高,以便玩家下次更容易猜对。同样,如果number小于random,我们告诉玩家猜测太低。否则,如果是正确的猜测,那么我们打印玩家用来做出正确猜测的总次数,并且我们从循环中退出。

请注意,你需要break语句来退出无限循环。没有break语句,循环将永远运行。

最后,我们每次猜错(高或低)都会将tries的数量增加 1:

tries=$(($tries+1))

我必须警告你,这个游戏很容易上瘾!特别是当你和朋友一起玩时,看谁能在最少的尝试次数中猜对。

使用until循环

forwhile循环都会在测试条件为true时运行。相反,until循环会在测试条件为false时继续运行。也就是说,它会在测试条件为true时停止运行。

until循环的一般语法如下:

until [condition is true]; do 
  [commands]
done

例如,我们可以创建一个简单的脚本3x10.sh,打印出3的前十个倍数:

elliot@ubuntu-linux:~$ cat 3x10.sh 
#!/bin/bash
counter=1
until [ $counter -gt 10 ]; do 
echo $(($counter * 3)) 
counter=$(($counter+1))
done

现在让脚本可执行,然后运行它:

elliot@ubuntu-linux:~$ chmod a+x 3x10.sh 
elliot@ubuntu-linux:~$ 3x10.sh
3
6
9
12
15
18
21
24
27
30

脚本很容易理解,但你可能会在尝试理解until循环的测试条件时有点困惑:

until [ $counter -gt 10 ]; do

测试条件基本上是这样说的:“直到counter大于 10,继续运行!”

请注意,我们可以使用具有相反测试条件的while循环来实现相同的结果。你只需否定until循环的测试条件,就会得到while循环的等价形式:

while [ $counter -le 10 ]; do

在数学中,大于(>)的相反(否定)是小于或等于()。很多人忘记了等于部分。不要成为那些人中的一个!

Bash 脚本函数

当你的脚本变得越来越大时,事情可能会变得非常混乱。为了克服这个问题,你可以使用 bash 函数。函数的理念是你可以重用脚本的部分,从而产生更有组织和可读性的脚本。

bash 函数的一般语法如下:

function_name () {
<commands>
}

让我们创建一个名为hello的函数,打印出“Hello World”这一行。我们将hello函数放在一个名为fun1.sh的新脚本中:

elliot@ubuntu-linux:~$ cat fun1.sh 
#!/bin/bash

hello () {
echo "Hello World"
}

hello     # Call the function hello() 
hello     # Call the function hello() 
hello     # Call the function hello()

现在让脚本可执行,然后运行它:

elliot@ubuntu-linux:~$ chmod a+x fun1.sh 
elliot@ubuntu-linux:~$ ./fun1.sh
Hello World 
Hello World 
Hello World

该脚本将在终端上输出“Hello World”三次。请注意,我们调用(使用)了函数hello三次。

传递函数参数

函数也可以像脚本一样接受参数。为了演示,我们将创建一个名为math.sh的脚本,其中有两个函数addsub

elliot@ubuntu-linux:~$ cat math.sh 
#!/bin/bash

add () {
echo "$1 + $2 =" $(($1+$2))
}

sub () {
echo "$1 - $2 =" $(($1-$2))
}

add 7 2
sub 7 2

使脚本可执行,然后运行它:

elliot@ubuntu-linux:~$ chmod a+x math.sh 
elliot@ubuntu-linux:~$ ./math.sh
7 + 2 = 9
7 - 2 = 5

该脚本有两个函数addsubadd函数计算并输出任意两个数字的总和。另一方面,sub函数计算并输出任意两个数字的差。

你不能浏览网页

我们将用一个相当酷的 bash 脚本noweb.sh来结束本章,确保没有用户在 Firefox 浏览器上浏览网页时玩得开心:

elliot@ubuntu-linux:~$ cat noweb.sh 
#!/bin/bash

shutdown_firefox() { 
killall firefox 2> /dev/null
}

while [ true ]; do 
shutdown_firefox
sleep 10 #wait for 10 seconds 
done

现在将 Firefox 作为后台进程打开:

elliot@ubuntu-linux:~$ firefox & 
[1] 30436

最后,使脚本可执行,并在后台运行脚本:

elliot@ubuntu-linux:~$ chmod a+x noweb.sh 
elliot@ubuntu-linux:~$ ./noweb.sh &
[1] 30759

一旦运行你的脚本,Firefox 就会关闭。此外,如果以root用户身份运行脚本,系统用户将无法享受 Firefox!

知识检查

对于以下练习,打开你的终端并尝试解决以下任务:

  1. 创建一个 bash 脚本,显示当前月份的日历。

  2. 修改你的脚本,以便显示任何年份(作为参数传递)的日历。

  3. 修改你的脚本,以便显示从2000年到2020年的所有年份的日历。

您需要一个 Cron 作业

在本章中,您将学习如何通过使用 cron 作业自动化 Linux 中的乏味任务,这是 Linux 中最有用和强大的实用程序之一。由于 cron 作业,Linux 系统管理员可以在周末休息,并与他们所爱的人一起度假。Cron 作业允许您安排任务在特定时间运行。使用 cron 作业,您可以安排运行备份,监视系统资源等任务。

第十八章:我们的第一个 cron 作业

以下图表显示了 cron 作业的典型格式:

图 1:Cron 作业格式

Cron 作业是特定于用户的,因此每个用户都有自己的 cron 作业列表。例如,用户elliot可以运行命令crontab -l来显示他们的 cron 作业:

elliot@ubuntu-linux:~$ crontab -l 
no crontab for elliot

目前,用户elliot没有任何 cron 作业。

让我们继续创建 Elliot 的第一个 cron 作业。我们将创建一个每分钟运行一次的 cron 作业,它将简单地将“一分钟已经过去。”这一行追加到文件/home/elliot/minutes.txt中。

您可以运行命令crontab -e来编辑或创建 cron 作业:

elliot@ubuntu-linux:~$ crontab -e

现在添加以下行,然后保存并退出:

* * * * * echo "A minute has passed." >> /home/elliot/minutes.txt

退出后,您将看到消息:“crontab:正在安装新的 cron 表”:

elliot@ubuntu-linux:~$ crontab -e 
crontab: installing new crontab

最后,用户elliot可以列出他们的 cron 作业,以验证新的 cron 作业是否已安排:

elliot@ubuntu-linux:~$ crontab -l
* * * * * echo "A minute has passed." >> /home/elliot/minutes.txt

现在,等待几分钟,然后检查文件/home/el- liot/minutes.txt的内容:

elliot@ubuntu-linux:~$ cat /home/elliot/minutes.txt 
A minute has passed.
A minute has passed. 
A minute has passed. 
A minute has passed. 
A minute has passed.

我等了五分钟,然后查看文件,看到“一分钟已经过去。”这一行被添加了五次到文件minutes.txt中,所以我知道 cron 作业运行正常。

每五分钟运行一次

让我们创建另一个每五分钟运行一次的 cron 作业。例如,您可能希望创建一个每五分钟检查系统负载平均值的 cron 作业。

运行命令crontab -e以添加新的 cron 作业:

elliot@ubuntu-linux:~$ crontab -e

现在添加以下行,然后保存并退出:

*/5 * * * * uptime >> /home/elliot/load.txt

最后,让我们查看已安装的 cron 作业列表,以验证新的 cron 作业是否已安排:

elliot@ubuntu-linux:~$ crontab -e 
crontab: installing new crontab 
elliot@ubuntu-linux:~$ crontab -l
* * * * * echo "A minute has passed" >> /home/elliot/minutes.txt
*/5 * * * * uptime >> /home/elliot/load.txt

现在我们可以看到为用户elliot安装了两个 cron 作业。

等待五到十分钟,然后检查文件/home/elliot/load.txt的内容。如果您没有秒表,运行命令sleep 300并等待直到完成:

elliot@ubuntu-linux:~$ sleep 300

我给自己泡了一些绿茶,然后十分钟后回来查看文件/home/elliot/load.txt

elliot@ubuntu-linux:~$ cat /home/elliot/load.txt
14:40:01 up 1 day, 5:13, 2 users, load average: 0.41, 0.40, 0.37
14:45:01 up 1 day, 5:18, 2 users, load average: 0.25, 0.34, 0.35

预期内,这个 cron 作业在这十分钟内运行了两次;我建议您再过二十四小时再次查看文件/home/elliot/load.txt,您将看到一份关于系统负载平均值的可爱报告。

更多 cron 示例

您还可以安排 cron 作业以在多个时间间隔运行。例如,以下 cron 作业将在星期日的每个小时的52040分钟运行:

5,20,40 * * * sun task-to-run

您还可以指定时间范围。例如,一个在工作日(星期一->星期五)的6:30 PM 运行的 cron 作业将具有以下格式:

30 18 * * 1-5 task-to-run

注意0是星期日,1是星期一,依此类推。

要查看更多 cron 示例,可以查看crontab手册的第五部分:

elliot@ubuntu-linux:~$ man 5 crontab

自动化系统打补丁

作为 Linux 系统管理员,您经常需要打补丁(更新)系统。有时,生产服务器安排在不愉快的时间更新,比如周末的午夜,凌晨04:00,凌晨02:00等,这可能会让您发疯。自动化这样繁忙的任务并多睡一会儿会很好,对吧?

让我们切换到root用户,然后创建一个名为auto_patch.sh的 bash 脚本

/root中:

root@ubuntu-linux:~# cat auto_patch.sh 
#!/bin/bash
apt-get -y update 
apt-get -y upgrade 
shutdown -r now

注意脚本auto_patch.sh很小,只有三行。我们已经使用了

-y选项与apt-get命令一起使用,这将自动回答系统更新期间的所有提示为“是”;这很重要,因为在脚本运行时你不会坐在电脑前!

现在使脚本可执行:

root@ubuntu-linux:~# chmod +x auto_patch.sh

最后,您需要安排一个 cron 作业来运行auto_patch.sh脚本。假设系统已安排在每周六凌晨 01:00 更新。在这种情况下,您可以创建以下 cron 作业:

0 1 * * sat /root/auto_patch.sh

请记住,auto_patch.sh永远不会部署在任何真实的服务器上。我只是在向您介绍自动化的概念。您需要编辑auto_patch.sh以检查命令退出代码,因为期望一切都会顺利进行是天真的。一个优秀的系统管理员总是创建能处理各种预期错误的健壮脚本。

运行一次作业

您必须在auto_patch.sh运行后的某个时间删除 cron 作业,否则它将每周继续更新系统!为此,还存在另一个专门用途的实用程序at;即,安排运行一次作业。

我们首先需要安装at软件包:

root@ubuntu-linux:~# apt-get -y install at

现在,您可以安排在本周六凌晨 01:00 运行auto_patch.sh脚本,使用以下命令:

root@ubuntu-linux:~# at 01:00 AM Sat -f /root/patch.sh

请记住,at作业只运行一次,因此在周六之后,auto_patch.sh脚本将不会再次运行。

您可以通过阅读其手册页了解更多关于at的信息:

root@ubuntu-linux:~# man at

知识检查

对于以下练习,打开您的终端并尝试解决以下任务:

  1. 为 root 用户创建一个 cron 作业,每 10 分钟运行一次。该 cron 作业将简单地将行“已经过去 10 分钟!”附加到文件/root/minutes.txt中。

  2. 为 root 用户创建一个 cron 作业,每年圣诞节(12 月 25 日凌晨 1 点)运行一次。该 cron 作业将简单地将行“圣诞快乐!”附加到文件/root/holidays.txt中。

存档和压缩文件

在本章中,您将学习如何将一组文件放在一个单独的存档中。您还将学习如何使用各种压缩方法压缩存档文件。

第十九章:创建存档

让我们为/home/elliot目录中的所有 bash 脚本创建一个备份。作为root用户,在/root中创建一个名为backup的目录:

root@ubuntu-linux:~# mkdir /root/backup

要创建存档,我们使用磁带存档命令tar。创建存档的一般语法如下:

tar -cf archive_name files

-c选项是--create的简写,用于创建存档。-f选项是--file的简写,用于指定存档名称。

现在让我们在/root/backup中为/home/elliot中的所有 bash 脚本创建一个名为scripts.tar的存档。为此,我们首先切换到/home/elliot目录:

root@ubuntu-linux:~# cd /home/elliot 
root@ubuntu-linux:/home/elliot#

然后我们运行命令:

root@ubuntu-linux:/home/elliot# tar -cf /root/backup/scripts.tar *.sh

这将在/root/backup中创建存档文件scripts.tar,并且不会有命令输出:

root@ubuntu-linux:/home/elliot# ls -l /root/backup/scripts.tar
-rw-r--r-- 1 root root 20480 Nov 1 23:12 /root/backup/scripts.tar

我们还可以添加-v选项以查看正在存档的文件:

root@ubuntu-linux:/home/elliot# tar -cvf /root/backup/scripts.tar *.sh 
3x10.sh
detect.sh 
empty.sh 
filetype.sh 
fun1.sh 
game.sh 
hello20.sh 
hello2.sh 
hello3.sh 
hello.sh 
math.sh 
mydate.sh 
noweb.sh 
numbers.sh 
rename.sh 
size2.sh 
size3.sh 
size.sh

查看存档内容

您可能想要查看存档的内容。为此,您可以使用-t选项以及后跟存档名称的-f选项:

tar -tf archive

例如,要查看我们刚刚创建的scripts.tar存档的内容,可以运行以下命令:

root@ubuntu-linux:/home/elliot# tar -tf /root/backup/scripts.tar 
3x10.sh
detect.sh 
empty.sh 
filetype.sh 
fun1.sh 
game.sh 
hello20.sh 
hello2.sh 
hello3.sh 
hello.sh 
math.sh 
mydate.sh 
noweb.sh 
numbers.sh 
rename.sh 
size2.sh 
size3.sh 
size.sh

如您所见,它列出了scripts.tar存档中的所有文件。

提取存档文件

您可能还想从存档中提取文件。为了演示,让我们在/root中创建一个名为myscripts的目录:

root@ubuntu-linux:/# mkdir /root/myscripts

要从存档中提取文件,我们使用-x选项以及后跟存档名称的-f选项。然后,我们使用-C选项,后跟目标目录,如下所示:

tar -xf archive -C destination

因此,要将scripts.tar存档中的所有文件提取到/root/myscripts目录中,您可以运行以下命令:

root@ubuntu-linux:/# tar -xf /root/backup/scripts.tar -C /root/myscripts

-x选项是--extract的简写,用于从存档中提取文件。我们还使用了-C选项,它在执行任何操作之前基本上会切换到/root/myscripts目录,因此文件被提取到/root/myscripts而不是当前目录。

现在让我们验证文件确实提取到了/root/myscripts目录中:

root@ubuntu-linux:/# ls /root/myscripts
3x10.sh 
empty.sh 
fun1.sh 
hello20.sh 
hello3.sh 
math.sh 
noweb.sh 
rename.sh 
size3.sh 
detect.sh 
filetype.sh 
game.sh 
hello2.sh 
hello.sh 
mydate.sh 
numbers.sh 
size2.sh 
size.sh

果然,我们在/root/myscripts目录中看到了所有的 bash 脚本!

使用 gzip 进行压缩

单独将文件放在存档中并不会节省磁盘空间。我们需要压缩存档以节省磁盘空间。在 Linux 上有许多压缩方法可供我们使用。但是,我们只将介绍三种最流行的压缩方法。

在 Linux 上最受欢迎的压缩方法可能是gzip,好处是它非常快速。您可以使用tar命令的-z选项将存档文件压缩为gzip,如下所示:

tar -czf compressed_archive archive_name

因此,要将scripts.tar存档压缩为名为scripts.tar.gzgzip压缩存档,您首先需要切换到/root/backup目录,然后运行以下命令:

root@ubuntu-linux:~/backup# tar -czf scripts.tar.gz scripts.tar

现在,如果列出backup目录的内容,您将看到新创建的gzip压缩存档scripts.tar.gz

root@ubuntu-linux:~/backup# ls 
scripts.tar scripts.tar.gz

通过使用-z选项进行了魔术操作,该选项使用gzip压缩方法压缩了存档。就是这样!请注意,这与创建存档非常相似:我们只是添加了-z选项-这是唯一的区别。

现在让我们在两个存档上运行file命令:

root@ubuntu-linux:~/backup# file scripts.tar 
scripts.tar: POSIX tar archive (GNU) 
root@ubuntu-linux:~/backup# file scripts.tar.gz
scripts.tar.gz: gzip compressed data, last modified: Sat Nov 2 22:13:44 2019, 
from Unix

如您所见,file命令检测到了两个存档的类型。现在让我们比较一下两个存档的大小(以字节为单位):

root@ubuntu-linux:~/backup# du -b scripts.tar scripts.tar.gz 
20480 scripts.tar
1479 scripts.tar.gz

与未压缩存档scripts.tar相比,压缩存档scripts.tar.gz的大小要小得多,这是我们预期的。如果要将压缩存档scripts.tar.gz中的文件提取到/root/myscripts,可以运行:

root@ubuntu-linux:~/backup# tar -xf scripts.tar.gz -C /root/myscripts

请注意,这与提取未压缩存档的内容的方式完全相同。

使用 bzip2 进行压缩

bzip2是 Linux 上另一种流行的压缩方法。平均而言,bzip2gzip慢;然而,bzip2在将文件压缩到更小的大小方面做得更好。

你可以使用tar命令的-j选项来使用bzip2压缩压缩存档,如下所示:

tar -cjf compressed_archive archive_name

注意这里唯一的区别是我们使用bzip2压缩的-j选项,而不是gzip压缩的-z选项。

因此,要将scripts.tar存档压缩成名为scripts.tar.bz2bzip2压缩存档,你首先需要切换到/root/backup目录,然后运行以下命令:

root@ubuntu-linux:~/backup# tar -cjf scripts.tar.bz2 scripts.tar

现在,如果你列出backup目录的内容,你会看到新创建的bzip2压缩的存档scripts.tar.bz2

root@ubuntu-linux:~/backup# ls
scripts.tar scripts.tar.bz2 scripts.tar.gz

让我们在bzip2压缩的存档scripts.tar.bz2上运行file命令:

root@ubuntu-linux:~/backup# file scripts.tar.bz2 
scripts.tar.bz2: bzip2 compressed data, block size = 900k

它正确地检测到了用于存档scripts.tar.bz2的压缩方法。太棒了-现在让我们比较gzip压缩的存档scripts.tar.gzbzip2压缩的存档scripts.tar.bz2的大小(以字节为单位):

root@ubuntu-linux:~/backup# du -b scripts.tar.bz2 scripts.tar.gz 
1369 scripts.tar.bz2
1479 scripts.tar.gz

注意bzip2压缩的存档scripts.tar.bz2gzip压缩的存档scripts.tar.gz要小。如果你想要将压缩存档scripts.tar.bz2中的文件提取到/root/myscripts,你可以运行:

root@ubuntu-linux:~/backup# tar -xf scripts.tar.bz2 -C /root/myscripts

注意它与提取gzip压缩的存档的内容的方式完全相同。

使用 xz 压缩

xz压缩方法是 Linux 上另一种流行的压缩方法。平均而言,xz压缩在减小(压缩)文件大小方面做得比所有三种压缩方法中的其他方法都要好。

你可以使用tar命令的-J选项来使用xz压缩压缩存档,如下所示:

tar -cJf compressed_name archive_name

注意这里我们使用大写字母Jxz压缩。因此,要将scripts.tar存档压缩成名为scripts.tar.xzxz压缩存档,你首先需要切换到/root/backup目录,然后运行以下命令:

root@ubuntu-linux:~/backup# tar -cJf scripts.tar.xz scripts.tar

现在,如果你列出backup目录的内容,你会看到新创建的xz压缩的存档scripts.tar.xz

root@ubuntu-linux:~/backup# ls
scripts.tar scripts.tar.bz2 scripts.tar.gz scripts.tar.xz

让我们在scripts.tar.xz上运行file命令:

root@ubuntu-linux:~/backup# file scripts.tar.xz 
scripts.tar.xz: XZ compressed data

它正确地检测到了用于存档scripts.tar.xz的压缩方法。

性能测量

你可以使用time命令来测量命令(或程序)执行所需的时间。time命令的一般语法如下:

time command_or_program

例如,要测量date命令执行所需的时间,你可以运行以下命令:

root@ubuntu-linux:~# time date 
Sun Nov 3 16:36:33 CST 2019

real 0m0.004s 
user 0m0.003s 
sys 0m0.000s

在我的系统上运行date命令只用了四毫秒;这相当快!

gzip压缩方法是所有三种压缩方法中最快的;好吧,让我们看看我是在撒谎还是在说实话!切换到/root/backup目录:

root@ubuntu-linux:~# cd /root/backup 
root@ubuntu-linux:~/backup#

现在让我们看看为/boot中的所有文件创建一个gzip压缩的存档文件需要多长时间:

root@ubuntu-linux:~/backup# time tar -czf boot.tar.gz /boot 
real 0m4.717s
user 0m4.361s 
sys 0m0.339s

在我的系统上,运行gzip花了 4.717 秒!现在让我们测量创建相同目录/bootbzip2压缩存档所需的时间:

root@ubuntu-linux:~/backup# time tar -cjf boot.tar.bz2 /boot 
real 0m19.306s
user 0m18.809s 
sys   0m0.359s

bzip2花了巨大的 19.306 秒!你可以看到gzip压缩比bzip2快得多。现在让我们看看创建相同目录/bootxz压缩存档所需的时间:

root@ubuntu-linux:~/backup# time tar -cJf boot.tar.xz /boot 
real 0m53.745s
user 0m52.679s 
sys   0m0.873s

xz几乎花了整整一分钟!我们可以得出结论,gzip绝对是我们讨论的所有三种压缩方法中最快的。

最后,让我们检查三个压缩存档的大小(以字节为单位):

root@ubuntu-linux:~/backup# du -b boot.* 
97934386 boot.tar.bz2
98036178 boot.tar.gz
94452156 boot.tar.xz

正如你所看到的,xz在压缩文件方面做得最好。bzip2排名第二,gzip排名最后。

知识检查

对于以下练习,打开你的终端并尝试解决以下任务:

  1. /root中为/var中的所有文件创建一个名为var.tar.gzgzip存档。

  2. /root中为/tmp中的所有文件创建一个名为tmp.tar.bz2bzip2存档。

  3. /root目录中为/etc目录中的所有文件创建一个名为etc.tar.xzxz归档文件。

创建你自己的命令

有时,你可能会很难记住一个命令。其他时候,你会发现自己一遍又一遍地运行一个非常长的命令,这让你发疯。在本章中,你将学习如何创建自己的命令,因为你才是真正的老板。

第二十章:你的第一个别名

假设你总是忘记free -h命令显示系统的内存信息:

elliot@ubuntu-linux:~$ free -h
 total     used     free     shared     buff/cache     available
Mem:       3.9G     939M     2.2G       6.6M           752M         2.7G
Swap:      947M       0B     947M 

也许你会问自己:“为什么我不能只输入memory来显示内存信息,而不是free -h?”。好吧,你当然可以通过创建一个alias来做到这一点。

alias命令指示 shell 用另一个字符串(单词)替换一个字符串。那么这有什么用呢?让我告诉你;如果你运行以下命令:

elliot@ubuntu-linux:~$ alias memory="free -h"

那么每次你输入memory,你的 shell 都会将其替换为free -h

elliot@ubuntu-linux:~$ memory
 total     used     free     shared     buff/cache     available
Mem:       3.9G     936M     2.2G       6.6M           756M         2.7G
Swap:      947M       0B     947M

哇!现在你已经实现了你的梦想!你可以为任何你难以记住的 Linux 命令创建一个别名。请注意,alias命令的一般格式如下:

alias alias_name="command(s)_to_run"

一个别名对应多个命令

你可以使用分号在同一行上运行多个命令。例如,要一次创建一个名为newdir的新目录并切换到newdir,你可以运行以下命令:

elliot@ubuntu-linux:~$ mkdir newdir; cd newdir 
elliot@ubuntu-linux:~/newdir$

你可以使用分号来分隔每个命令。一般来说,运行同一行上的多个命令的语法如下:

command1; command2; command3; command4; ....

我们经常想同时查看日历和日期,对吧?为此,我们将创建一个名为date的别名,这样每次运行date时,它将同时运行datecalendar命令:

elliot@ubuntu-linux:~$ alias date="date;cal"

现在让我们运行date,看看发生了什么:

请注意,这里我们使用了别名date,这已经是一个现有命令的名称;这对于别名来说是完全可以的。

列出所有别名

你还应该知道,别名是特定于用户的。因此,由elliot创建的别名对smurf用户不起作用;看一下:

elliot@ubuntu-linux:~$ su - smurf 
Password:
smurf@ubuntu-linux:~$ date 
Mon Nov 4 13:33:36 CST 2019
smurf@ubuntu-linux:~$ memory
Command 'memory' not found, did you mean: 
 command 'lmemory' from deb lmemory
Try: apt install <deb name>

正如你所看到的,smurf不能使用用户elliot的别名。所以每个用户都有自己的一组别名。现在,让我们退出到用户elliot

smurf@ubuntu-linux:~$ exit 
logout
elliot@ubuntu-linux:~$ memory
 total     used     free     shared     buff/cache     available
Mem:      3.9G     937M     2.0G       6.6M      990M               2.7G
Swap:     947M       0B     947M 

你可以运行alias命令来列出当前登录用户可以使用的所有别名:

elliot@ubuntu-linux:~$ alias 
alias date='date;cal'
alias egrep='egrep --color=auto' 
alias fgrep='fgrep --color=auto' 
alias grep='grep --color=auto' 
alias l='ls -CF'
alias la='ls -A' 
alias ll='ls -alF'
alias ls='ls --color=auto' 
alias memory='free -h'

创建永久别名

到目前为止,我们一直在创建临时别名;也就是说,我们创建的memorydate这两个别名是临时的,只在当前终端会话中有效。一旦关闭终端,这两个别名就会消失。

打开一个新的终端会话,然后尝试运行我们创建的两个别名:

elliot@ubuntu-linux:~$ date 
Mon Nov 4 13:43:46 CST 2019
elliot@ubuntu-linux:~$ memory

Command 'memory' not found, did you mean: 
 command 'lmemory' from deb lmemory
Try: sudo apt install <deb name>

如你所见,它们都消失了!它们甚至不再在你的别名列表中了:

elliot@ubuntu-linux:~$ alias 
alias egrep='egrep --color=auto' 
alias fgrep='fgrep --color=auto' 
alias grep='grep --color=auto' 
alias l='ls -CF'
alias la='ls -A' 
alias ll='ls -alF'
alias ls='ls --color=auto'

要为用户创建一个永久别名,你需要将其包含在用户主目录中的隐藏.bashrc文件中。因此,要永久添加我们的两个别名,你必须在/home/el- liot/.bashrc文件的最末尾添加以下两行:

alias memory = "free -h" 
alias date = "date;cal"

你可以通过运行以下两个echo命令来实现:

elliot@ubuntu-linux:~$ echo 'alias memory="free -h"' >> /home/elliot/.bashrc 
elliot@ubuntu-linux:~$ echo 'alias date="date;cal"' >> /home/elliot/.bashrc

在将这两个别名添加到/home/elliot/.bashrc文件后,你需要在/home/elliot/.bashrc文件上运行source命令,以使当前会话中的更改生效:

elliot@ubuntu-linux:~$ source /home/elliot/.bashrc

现在你可以永远使用你的两个别名memorydate,而不必担心在关闭当前终端会话后它们会消失:

删除别名

让我们创建另一个临时别名,名为lastline,它将显示文件中的最后一行:

elliot@ubuntu-linux:~$ alias lastline="tail -n 1"

现在让我们在/home/elliot/.bashrc文件上尝试我们的新别名:

elliot@ubuntu-linux:~$ lastline /home/elliot/.bashrc 
alias date="date;cal"

好了!它运行得很好。现在,如果你想删除别名,你可以运行unalias命令,然后跟上别名:

elliot@ubuntu-linux:~$ unalias lastline

现在lastline别名已经被删除:

elliot@ubuntu-linux:~$ lastline /home/elliot/.bashrc 
lastline: command not found

你还可以使用unalias命令临时停用永久别名。例如,如果你运行以下命令:

elliot@ubuntu-linux:~$ unalias memory

现在,永久别名memory将在当前终端会话中不起作用:

elliot@ubuntu-linux:~$ memory

Command 'memory' not found, did you mean: 
 command 'lmemory' from deb lmemory
Try: sudo apt install <deb name>

然而,别名memory将在新的终端会话中回来。要删除永久别名,你需要从.bashrc文件中删除它。

一些有用的别名

现在让我们创建一些有用的别名,让我们在 Linux 命令行上工作时更加愉快。

很多人讨厌记住所有tar命令选项,所以让我们为这些人简化一下。我们将创建一个名为extract的别名,它将从存档中提取文件。

elliot@ubuntu-linux:~$ alias extract="tar -xvf"

你可以在任何存档上尝试这个别名,它会像魔术一样起作用。

同样地,你可以创建一个名为compress_gzip的别名,它将创建一个 gzip 压缩的存档:

elliot@ubuntu-linux:~$ alias compress_gzip="tar -czvf"

你可能还想创建一个名为soft的别名,它将创建软链接:

elliot@ubuntu-linux:~$ alias soft="ln -s"

你可以使用软别名来创建一个名为logfiles的软链接,指向/var/logs目录:

elliot@ubuntu-linux:~$ soft /var/logs logfiles 
elliot@ubuntu-linux:~$ ls -l logfiles
lrwxrwxrwx 1 elliot elliot 9 Nov 4 15:08 logfiles -> /var/logs

现在让我们创建一个名为LISTEN的别名,它将列出系统上所有正在监听的端口:

elliot@ubuntu-linux:~$ alias LISTEN="netstat -tulpen| grep -i listen"

现在让我们尝试运行LISTEN别名:

elliot@ubuntu-linux:~$ LISTEN
tcp     0     0 127.0.0.53:53     0.0.0.0:*     LISTEN
tcp     0     0 0.0.0.0:22        0.0.0.0:*     LISTEN
tcp     0     0 127.0.0.1:631     0.0.0.0:*     LISTEN
tcp     0     0 127.0.0.1:25      0.0.0.0:*     LISTEN
tcp6    0     0 :::22             :::*          LISTEN
tcp6    0     0 ::1:631           :::*          LISTEN
tcp6    0     0 ::1:25            :::*          LISTEN

这很酷!让我们创建一个最终别名sort_files,它将按大小(按降序)列出当前目录中的所有文件:

alias sort_files="du -bs * | sort -rn"

现在让我们尝试运行sort_files别名:

elliot@ubuntu-linux:~$ sort_files 
9628732 Downloads
2242937 Pictures
65080 minutes.txt
40393 load.txt
32768 dir1
20517 Desktop
20480 small
8192 hackers
476 game.sh
168 practise.txt
161 filetype.sh
142 noweb.sh
108 3x10.sh
92 rename.sh
92 numbers.sh
88 detect.sh
74 hello3.sh
66 fun1.sh
59 hello20.sh
37 hello2.sh
33 hello.sh
17 mydate.sh
16 honey
9 logs
6 softdir1
0 empty

如你所见,当前目录中的文件按大小(即最大的文件先)降序列出。当你在系统上进行清理并想要检查哪些文件占用了最多空间时,这将特别有用。

添加安全网

你也可以使用别名来防止愚蠢的错误。例如,为了防止误删重要文件,你可以添加以下别名:

elliot@ubuntu-linux:~$ alias rm="rm -i"

现在每次你尝试删除一个文件时都会被要求确认:

elliot@ubuntu-linux:~$ rm *
rm: remove regular file '3x10.sh'?

用别名发疯

你也可以通过别名来玩一些有趣的东西,让用户发疯;看看这个别名:

elliot@ubuntu-linux:~$ alias nano="vi"

现在当用户elliot尝试打开nano编辑器时,将会打开vi编辑器!用户elliot可以通过输入nano编辑器的完整路径来克服这个困境。这里是另一个有趣的别名:

elliot@ubuntu-linux:~$ alias exit="echo No I am not exiting ..."

现在看看当用户elliot尝试退出终端时会发生什么:

elliot@ubuntu-linux:~$ exit 
No I am not exiting ... 
elliot@ubuntu-linux:~$ exit 
No I am not exiting ...

我会让你自己处理这个问题;我就是这么邪恶!哈哈。

知识检查

对于以下练习,打开你的终端并尝试解决以下任务:

  1. apt-get install命令创建一个临时别名ins

  2. dpkg -l命令创建一个临时别名packages

  3. 创建一个名为clean的永久别名,它将删除/tmp目录中的所有文件。

每个人都需要磁盘空间

在本章中,您将学习如何在 Linux 中管理硬盘。您将学习如何在驱动器上创建新分区。然后您将学习如何创建和挂载文件系统。最后,您将学习如何使用 LVM 创建逻辑卷。

第二十一章:您的设备在哪里?

正如我们现在都知道的那样,在 Linux 中,文件代表一切,设备也不例外。所有设备都位于/dev目录中;这包括您的键盘、鼠标、终端、硬盘、USB 设备、CD-ROM 等。

您现在正在使用的终端实际上是一个设备。如果运行w命令,您将在输出的第二列中看到您连接到的终端的名称。

elliot@ubuntu-linux:~$ w
11:38:59 up 17 min, 1 user, load average: 0.00, 0.00, 0.02
USER   TTY       FROM             LOGIN@  IDLE  JCPU  PCPU  WHAT
elliot pts/0     172.16.1.67      11:22   0.00s 0.06s 0.00s w

在我的情况下,它是pts/0pts伪终端的缩写。现在,这个终端由文件/dev/pts/0表示:

elliot@ubuntu-linux:~$ ls -l /dev/pts/0
crw------- 1 elliot tty 136, 0 Nov 7 11:40 /dev/pts/0

我将把Hello Friend这一行回显到/dev/pts/0,并密切关注接下来会发生什么:

elliot@ubuntu-linux:~$ echo "Hello Friend" > /dev/pts/0 
Hello Friend

正如您所看到的,Hello Friend被打印到我的终端!现在您可以在系统上与其他用户玩这个游戏。您可以运行w命令来找出他们正在使用的终端,然后开始发送消息给他们!

您的硬盘在哪里?

要知道哪个文件代表您的硬盘,您需要运行lsblk命令,这是列出块的缩写:

elliot@ubuntu-linux:~$ lsblk
NAME   MAJ:MIN  RM SIZE RO  TYPE MOUNTPOINT
sda      8:0     0  20G  0  disk
| sda1   8:1     0  20G  0  part /
sr0      11:0    1 1024M 0  rom

从输出中,我可以看到我的硬盘设备的名称是sda,这是SCSI Disk A的缩写。现在您需要明白,根据您的硬盘驱动器类型,您可能会得到不同的名称。图 1总结了不同类型硬盘驱动器的 Linux 命名策略:

图 1:Linux 中的硬盘命名

因此,从lsblk命令的输出中,您可以得出结论,我在我的虚拟机上只有一个磁盘(sda)。现在我们不想操作这个磁盘,因为它包含了根文件系统,所以让我们为学习目的向我们的虚拟机添加另一个磁盘。

向虚拟机添加磁盘

要成功向虚拟机添加新磁盘,您需要遵循一些步骤。您必须按照特定顺序执行这些步骤:

  1. 关闭您的虚拟机。

  2. 转到虚拟机设置|存储|创建新磁盘。

  3. 启动您的虚拟机。

因此,第一步非常简单;关闭您的虚拟机,因为在虚拟机仍在运行时无法向其添加新磁盘。对于第二步,您需要进入虚拟机设置,然后点击存储,然后选择您的磁盘控制器,右键单击,然后按图 2所示创建新磁盘:

图 2:在虚拟机上创建新磁盘

然后您将被要求选择新磁盘的大小。您可以选择任何大小。我在我的主机机器上有大量的磁盘空间,所以我将向我的虚拟机添加一个 10 GB 的磁盘。完成后,最后一步是再次启动您的虚拟机。

只要您的虚拟机启动,您应该能够看到您的新磁盘:

elliot@ubuntu-linux:~$ lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda       8:0   0  20G  0  disk
| sda1    8:1   0  20G  0  part /
sdb       8:16  0  10G  0  disk
sr0      11:0   1 1024M 0  rom

我的新磁盘得到了名称sdb,因为它是我虚拟机上的第二个磁盘,您还可以看到它的大小为 10 GB。

创建新磁盘分区

现在让我们来玩一下我们刚刚创建的新磁盘。您可能想要做的第一件事是创建一个新分区。要创建一个新分区,我们使用fdisk命令,后面跟着磁盘名称:

fdisk [options] device

因此,要在/dev/sdb磁盘上创建一个新分区,可以运行以下命令:

root@ubuntu-linux:~# fdisk /dev/sdb

Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them. 
Be careful before using the write command.

Device does not contain a recognized partition table. 
Created a new DOS disklabel with disk identifier 0xb13d9b6a.

Command (m for help):

这打开了fdisk实用程序。如果你不确定该怎么做,可以输入m来获取帮助:

Command (m for help): m 
Help:
 DOS (MBR)
 a   toggle a bootable flag
 b   edit nested BSD disklabel
 c   toggle the dos compatibility flag

 Generic
 d   delete a partition
 F   list free unpartitioned space l list known partition types
 n   add a new partition
 p   print the partition table t change a partition type
 v   verify the partition table
 i   print information about a partition

 Save & Exit
 w   write table to disk and exit 
 q   quit without saving changes

 Create a new label
 g   create a new empty GPT partition table
 G   create a new empty SGI (IRIX) partition table 
 o   create a new empty DOS   partition table
 s   create a new empty Sun partition table

我们想创建一个新分区,所以输入n

Command (m for help): n 
Partition type
 p primary (0 primary, 0 extended, 4 free)
 e extended (container for logical partitions) 
Select (default p):

然后它会问你是要主分区还是扩展分区。我们会接受默认选择(主分区),所以只需按Enter

Using default response p. 
Partition number (1-4, default 1):

然后它会要求您选择一个分区号。我们也会接受默认值,即分区号1,所以只需按Enter。请注意,您可以在给定磁盘上创建最多四个主分区:

Partition number (1-4, default 1):
First sector (2048-20971519, default 2048):

然后您将被提示选择您希望新分区从哪个扇区开始;按Enter接受默认值(2048):

First sector (2048-20971519, default 2048):
Last sector, +sectors or +size{K,M,G,T,P} (2048-20971519, default 20971519):

现在您将被要求选择新分区的大小;我想要一个 2GB 的分区,所以我会输入+2G,然后按Enter

Last sector, +sectors or +size{K,M,G,T,P} (2048-20971519, default 20971519): +2G

Created a new partition 1 of type 'Linux' and of size 2 GiB. 
Command (m for help):

最后,您需要按w保存配置:

Command (m for help): w
The partition table has been altered. 
Calling ioctl() to re-read partition table. 
Syncing disks.

现在您可以运行lsblk来查看您刚刚创建的新分区:

root@ubuntu-linux:~# lsblk
NAME   MAJ:MIN RM SIZE  RO TYPE MOUNTPOINT
sda      8:0   0  20G   0  disk
| sda1   8:1   0  20G   0  part /
sdb      8:16  0  10G   0  disk
| sdb1   8:17  0  2G    0  part
sr0     11:0   1 1024M  0  rom

您可以看到 2GB 分区sdb1列在sdb下。您还可以使用fdisk命令的-l选项打印出磁盘的分区表:

root@ubuntu-linux:~# fdisk -l /dev/sdb
Disk /dev/sdb: 10 GiB, 10737418240 bytes, 20971520 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes 
I/O size (minimum/optimal): 512 bytes / 512 bytes 
Disklabel type: dos
Disk identifier: 0xb13d9b6a

Device      Boot Start    End Sectors  Size  Id Type
/dev/sdb1        2048 4196351 4194304   2G   83 Linux

创建新文件系统

我现在还不能在我的/dev/sdb1分区上创建文件和目录;首先,我需要创建一个文件系统。文件系统基本上决定了数据在磁盘(或分区)上的组织和存储方式。一个很好的类比是飞机上的乘客;航空公司不能让乘客(数据)自己在飞机(分区)上就座;那将是一团糟。

在 Linux 上有许多不同类型的文件系统可用。重要的是要注意,ext4xfs是最常用的文件系统。图 3向您展示了 Linux 支持的一些可用文件系统:

图 3:Linux 文件系统

您可以在文件系统的man页面中阅读每种 Linux 文件系统类型的描述:

root@ubuntu-linux:~# man filesystems

要创建文件系统,我们使用mkfs命令,它是 make filesystem 的缩写。mkfs命令的一般语法如下:

mkfs --type [fstype] disk_or_partition

现在让我们在我们的新分区/dev/sdb1上创建一个ext4文件系统:

root@ubuntu-linux:~# mkfs --type ext4 /dev/sdb1 
mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 524288 4k blocks and 131072 inodes 
Filesystem UUID: 61d947bb-0cd1-41e1-90e0-c9895b6de428 
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Allocating group tables: done 
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

我们已经在我们的分区/dev/sdb1上创建了一个ext4文件系统。我们可以通过在/dev/sdb1分区上运行file -s命令来验证我们的工作:

root@ubuntu-linux:~# file -s /dev/sdb1
/dev/sdb1: Linux rev 1.0 ext4 filesystem data,
UUID=61d947bb-0cd1-41e1-90e0-c9895b6de428 (extents) (64bit) (large files) (huge files)

如您所见,它显示/dev/sdb1分区上有一个ext4文件系统。

您可以使用wipefs命令来删除(擦除)文件系统。例如,如果您想要删除我们刚在/dev/sdb1上创建的ext4文件系统,可以运行以下命令:

root@ubuntu-linux:~# wipefs -a /dev/sdb1
/dev/sdb1: 2 bytes were erased at offset 0x00000438 (ext4): 53 ef

现在如果您在/dev/sdb1分区上重新运行file -s,您将看到没有文件系统签名:

root@ubuntu-linux:~# file -s /dev/sdb1
/dev/sdb1: data

让我们重新在/dev/sdb1上创建一个ext4文件系统并保留它:

root@ubuntu-linux:~# mkfs --type ext4 /dev/sdb1 
mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 524288 4k blocks and 131072 inodes 
Filesystem UUID: 811aef62-d9ca-4db3-b305-bd896d1c8545 
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Allocating group tables: done 
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

挂载文件系统

我们已经在分区/dev/sdb1上创建了一个ext4文件系统。现在我们需要在 Linux 目录树中的某个地方挂载我们的文件系统。

什么是挂载?

挂载是指将任何文件系统或任何存储设备(如 USB 闪存驱动器、CD 等)附加到目录的过程。

但是为什么我们需要挂载?我的意思是,我们刚刚在 2GB 分区/dev/sdb1上创建了一个ext4文件系统。我们不能直接开始在/dev/sdb1上创建文件吗?答案是否定的!请记住,/dev/sdb1只是一个代表分区的文件。

要挂载文件系统,我们使用如下的 mount 命令:

mount filesystem mount_directory

所以假设我们将使用文件系统/dev/sdb1来存储我们的游戏。在这种情况下,让我们创建一个新目录/games

root@ubuntu-linux:~# mkdir /games

现在唯一剩下的就是将我们的文件系统/dev/sdb1挂载到/games目录上:

root@ubuntu-linux:/# mount /dev/sdb1 /games

我们可以通过运行lsblk命令来验证我们的工作:

root@ubuntu-linux:~# lsblk
NAME   MAJ:MIN  RM SIZE RO TYPE MOUNTPOINT
sda      8:0    0  20G  0  disk
| sda1   8:1    0  20G  0  part /
sdb      8:16   0  10G  0  disk
| sdb1   8:17   0  2G   0  part /games
sr0     11:0    1 1024M 0  rom

如您所见,/dev/sdb1确实挂载在/games上。

您还可以使用mount命令本身来列出系统上已挂载的所有文件系统。例如,要验证/dev/sdb1是否挂载在/games上,可以运行以下命令:

root@ubuntu-linux:/# mount | grep sdb1
/dev/sdb1 on /games type ext4 (rw,relatime,data=ordered)

我们现在有 2GB 可供我们在/games中使用,您可以使用df命令来显示文件系统磁盘空间的使用情况:

root@ubuntu-linux:~# df -h /games
Filesystem     Size Used Avail Use% Mounted on
/dev/sdb1      2.0G 6.0M 1.8G   1%  /games

现在让我们在/games中创建三个文件:

root@ubuntu-linux:~# cd /games
root@ubuntu-linux:/games# touch game1 game2 game3

卸载文件系统

您还可以卸载(与挂载相反)文件系统。正如您可能已经猜到的,卸载是指分离文件系统或存储设备的过程。要卸载文件系统,您可以使用umount如下:

umount filesystem

切换到/games目录并尝试卸载/dev/sdb1文件系统:

root@ubuntu-linux:/games# umount /dev/sdb1 
umount: /games: target is busy.

哎呀!它说目标正忙!那是因为我在挂载点/games内;我将备份到上一级目录,然后再试一次:

root@ubuntu-linux:/games# cd .. 
root@ubuntu-linux:/# umount /dev/sdb1

这次成功了!您必须小心,永远不要在文件系统或任何存储设备正在被使用时卸载它;否则,您可能会丢失数据!

现在让我们验证文件系统/dev/sdb1确实已卸载:

root@ubuntu-linux:/# lsblk
NAME   MAJ:MIN RM SIZE  RO TYPE MOUNTPOINT
sda      8:0   0  20G   0  disk
| sda1   8:1   0  20G   0  part /
sdb      8:16  0  10G   0  disk
| sdb1   8:17  0  2G    0  part
sr0     11:0   1 1024M  0  rom
root@ubuntu-linux:/# mount | grep sdb1

是的!它肯定已经卸载了!现在让我们列出/games目录的内容:

root@ubuntu-linux:/# ls /games

什么都没有!但不要惊慌或担心!我们创建的三个文件仍然存在于/dev/sdb1文件系统中。我们需要重新挂载文件系统,然后您将看到这些文件:

root@ubuntu-linux:~# mount /dev/sdb1 /games 
root@ubuntu-linux:~# ls /games
game1 game2 game3 lost+found

永久挂载文件系统

mount命令只会临时挂载文件系统;也就是说,使用mount命令挂载的文件系统不会在系统重新启动后保留。如果要永久挂载文件系统,那么您需要将其包含在文件系统表文件/etc/fstab中。

/etc/fstab中的每个条目(或行)代表一个不同的文件系统,每行由以下六个字段组成:

  • filesystem

  • mount_dir

  • fstype

  • mount_options

  • dump

  • check_fs

因此,例如,要永久将我们的/dev/sdb1文件系统挂载到/games上,您需要在/etc/fstab中包含以下行:

/dev/sdb1 /games ext4 defaults 0 0

您应该将该行添加到/etc/fstab文件的末尾:

root@ubuntu-linux:~# tail -1 /etc/fstab
/dev/sdb1   /games ext4   defaults   0   0

现在让我们卸载/dev/sdb1

root@ubuntu-linux:~# umount /dev/sdb1

最后,您现在可以通过运行以下命令永久挂载/dev/sdb1

root@ubuntu-linux:~# mount /dev/sdb1

请注意,这次我们没有指定挂载目标;那是因为挂载目标已经在/etc/fstab文件中指定了。您可以在mount命令中使用-a选项:

root@ubuntu-linux:~# mount -a

要挂载/etc/fstab中包含的所有文件系统。它还用于检查语法错误。例如,如果您在/etc/fstab中写错了/dev/sdx1,而不是/dev/sdb1,它将显示以下错误:

root@ubuntu-linux:~# mount -a
mount: /games: special device /dev/sdx1 does not exist.

所有在/etc/fstab中指定的挂载点都是永久的,它们将在系统重新启动后保留。您还可以参考fstab手册页,了解有关/etc/fstab的更多信息:

root@ubuntu-linux:~# man fstab

空间用完

让我们创建巨大的文件,以消耗/games中所有可用的磁盘空间。

在 Linux 中快速创建大文件的一种方法是使用dd命令。为了演示,让我们首先切换到/games目录:

root@ubuntu-linux:~# cd /games 
root@ubuntu-linux:/games#

现在,您可以运行以下命令创建一个名为bigGame1GB 文件:

root@ubuntu-linux:/games# dd if=/dev/zero of=bigGame bs=1G count=1 
1+0 records in
1+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 1.44297 s, 744 MB/s

我们现在已经使用了/games中超过一半的可用空间:

root@ubuntu-linux:/games# df -h /games 
Filesystem    Size Used Avail Use% Mounted on
/dev/sdb1     2.0G 1.1G 868M  55%  /games

现在让我们尝试创建另一个名为bigFish的大小为3GB 的文件:

root@ubuntu-linux:/games# dd if=/dev/zero of=bigFish bs=1G count=3 
dd: error writing 'bigFish': No space left on device
1+0 records in
0+0 records out
1016942592 bytes (1.0 GB, 970 MiB) copied, 1.59397 s, 638 MB/s

我们遇到了错误,因为空间用完了:

root@ubuntu-linux:/games# df -h /games 
Filesystem    Size Used Avail Use% Mounted on
/dev/sdb1     2.0G 2.0G  0    100% /games

现在我们甚至无法创建一个带有Hello一词的小文件:

root@ubuntu-linux:/games# echo Hello > greeting.txt
-su: echo: write error: No space left on device

损坏和修复文件系统

在一些不幸的情况下,您可能会遇到系统由于文件系统损坏而无法启动的问题。在这种情况下,您必须修复文件系统,以便系统正常启动。我将向您展示如何损坏文件系统,然后向您展示如何修复它。

通过向其写入随机数据,轻松损坏文件系统。

以下命令肯定会损坏您的/dev/sdb1文件系统:

root@ubuntu-linux:/games# dd if=/dev/urandom of=/dev/sdb1 count=10k

您的/dev/sdb1文件系统现在已损坏!如果您不相信我,卸载它,然后再尝试重新挂载它:

root@ubuntu-linux:~# umount /dev/sdb1

好的,它成功卸载了!让我们看看它是否会挂载:

root@ubuntu-linux:~# mount /dev/sdb1 /games
mount: /games: wrong fs type, bad option, bad superblock on /dev/sdb1, 
missing codepage or helper program, or other error.

正如您所看到的,它无法挂载,因为它会输出错误消息。

恭喜!您的文件系统已损坏。现在我们能做什么?好吧,我们肯定可以修复它!

您可以使用文件系统检查命令fsck来检查和修复文件系统。因此,让我们在我们损坏的文件系统上运行fsck

root@ubuntu-linux:~# fsck /dev/sdb1 
fsck from util-linux 2.31.1
e2fsck 1.44.1 (24-Mar-2018)
/dev/sdb1 was not cleanly unmounted, check forced.
fsck.ext4: Inode checksum does not match inode while reading bad blocks inode 
This doesn't bode well, but we'll try to go on...
Pass 1: Checking inodes, blocks, and sizes 
Inode 1 seems to contain garbage. Clear<y>?

如您所见,它指出文件系统包含垃圾数据,并询问您是否要清除错误。您可以按Y,但它会一遍又一遍地询问您是否要清除每个修复的 inode!您可以使用-y选项来避免这种情况,该选项会在修复过程中自动回答是。

root@ubuntu-linux:~# fsck -y /dev/sdb1

运行时,您会看到屏幕上出现很多数字。不用担心!它正在修复您损坏的文件系统。它基本上是在浏览成千上万的 inode。

完成后,您可以重新运行fsck来验证文件系统现在已经干净:

root@ubuntu-linux:~# fsck /dev/sdb1 
fsck from util-linux 2.31.1
e2fsck 1.44.1 (24-Mar-2018)
/dev/sdb1: clean, 11/131072 files, 9769/524288 blocks

太棒了!现在让我们尝试挂载它:

root@ubuntu-linux:~# mount /dev/sdb1 /games

这次成功挂载了。任务完成!我们成功修复了文件系统。

LVM 来拯救

当文件系统的空间用完时,情况可能会变得非常糟糕。我们已经在/games中用完了空间,使用标准分区没有简单的解决方案来增加更多的空间。幸运的是,逻辑卷管理器LVM)为管理文件系统提供了更好的替代方案。

安装 LVM 软件包

在我们开始使用 LVM 之前,首先需要安装lvm2软件包:

root@ubuntu-linux:~# apt-get install lvm2

安装完成后,您可以运行lvm version命令来验证安装是否成功:

root@ubuntu-linux:~# lvm version
 LVM version:     2.02.176(2) (2017-11-03)
 Library version: 1.02.145 (2017-11-03)
 Driver version:  4.37.0

三层抽象

要理解 LVM 的工作原理,首先需要将其可视化。LVM 就像是由三层组成的蛋糕,如图 4所示。

图 4:LVM 可视化

物理卷构成了 LVM 蛋糕的第一(基础)层。物理卷可以是整个磁盘(/dev/sdb/dev/sdc等)或分区(/dev/sdb2/dev/sdc3等)。

卷组层是 LVM 蛋糕中的第二层,也是最大的一层,它位于物理卷层的顶部。一个卷组可以跨多个物理卷;也就是说,一个卷组可以由一个或多个物理卷组成。

逻辑卷层构成了 LVM 蛋糕的第三层,也是最后一层。多个逻辑卷可以属于同一个卷组,如图 4所示。最后,您可以在逻辑卷上创建文件系统。

创建物理卷

创建物理卷的步骤非常简单;您只需要一个磁盘或一个分区。我们已经创建了一个2GB 的分区/dev/sdb1。现在继续在/dev/sdb下创建三个大小为2GB 的分区。

最终结果应该是这样的:

root@ubuntu-linux:~# lsblk
NAME    MAJ:MIN  RM  SIZE  RO  TYPE MOUNTPOINT
sda       8:0    0   20G   0   disk
| sda1    8:1    0   20G   0   part /
sdb       8:16   0   10G   0   disk
| sdb1    8:17   0    2G   0   part /games
| sdb2    8:18   0    2G   0   part
| sdb3    8:19   0    2G   0   part
| sdb4    8:20   0    2G   0   part
sr0      11:0    1  1024M  0   rom

要创建物理卷,我们使用pvcreate命令,后面跟着一个磁盘或一个分区:

pvcreate disk_or_partition

我们将创建三个物理卷:/dev/sdb2/dev/sdb3/dev/sdb4。您可以使用一个命令创建所有三个:

root@ubuntu-linux:~# pvcreate /dev/sdb2 /dev/sdb3 /dev/sdb4 
 Physical volume "/dev/sdb2" successfully created.
 Physical volume "/dev/sdb3" successfully created.
 Physical volume "/dev/sdb4" successfully created.

很酷!您还可以使用pvs命令列出所有物理卷:

root@ubuntu-linux:~# pvs
 PV       VG Fmt Attr PSize PFree
/dev/sdb2    lvm2 --- 2.00g 2.00g
/dev/sdb3    lvm2 --- 2.00g 2.00g
/dev/sdb4    lvm2 --- 2.00g 2.00g

好了!到目前为止一切看起来都很好。

创建卷组

一个卷组可以跨多个物理卷。因此,让我们创建一个卷组,它将由两个物理卷/dev/sdb2/dev/sdb3组成。

要创建卷组,我们使用vgcreate命令,后面跟着新卷组的名称,然后是物理卷:

vgcreate vg_name PV1 PV2 PV3 ...

让我们创建一个名为myvg的卷组,它将跨越/dev/sdb2/de- v/sdb3

root@ubuntu-linux:~# vgcreate myvg /dev/sdb2 /dev/sdb3 
 Volume group "myvg" successfully created

太棒了!您还可以使用vgs命令列出所有卷组:

root@ubuntu-linux:~# vgs
 VG   #PV #LV #SN Attr   VSize VFree 
 myvg   2   0   0 wz--n- 3.99g 3.99g

请注意,卷组myvg的大小等于4GB,这是/dev/sdb2/dev/sdb3的总大小。

创建逻辑卷

现在我们可以在我们的mvg卷组上创建逻辑卷。

要创建逻辑卷,我们使用lvcreate命令,后面跟着逻辑卷的大小、逻辑卷的名称,最后是卷组名称:

lvcreate --size 2G --name lv_name vg_name

让我们创建一个名为mybooks的逻辑卷,大小为2GB:

root@ubuntu-linux:~# lvcreate --size 2G --name mybooks myvg 
 Logical volume "mybooks" created.

现在创建另一个名为myprojects的逻辑卷,大小为500MB:

root@ubuntu-linux:~# lvcreate --size 500M --name myprojects myvg 
 Logical volume "myprojects" created.

您可以使用lvs命令列出所有逻辑卷:

root@ubuntu-linux:~# lvs
 LV         VG   Attr       LSize Pool Origin Data% Meta% Move Log 
 mybooks    myvg -wi-a----- 2.00g
 myprojects myvg -wi-a----- 500.00m

还有最后一步,就是在我们的逻辑卷上创建文件系统。

你的逻辑卷在设备映射目录/dev/mapper中表示:

root@ubuntu-linux:~# ls /dev/mapper 
myvg-mybooks myvg-myprojects

让我们在mybooks逻辑卷上创建一个ext4文件系统:

root@ubuntu-linux:~# mkfs --type ext4 /dev/mapper/myvg-mybooks 
mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 524288 4k blocks and 131072 inodes 
Filesystem UUID: d1b43462-6d5c-4329-b027-7ee2ecebfd9a 
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Allocating group tables: done 
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

同样地,我们可以在myprojects逻辑卷上创建一个ext4文件系统:

root@ubuntu-linux:~# mkfs --type ext4 /dev/mapper/myvg-myprojects 
mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 512000 1k blocks and 128016 inodes 
Filesystem UUID: 5bbb0826-c845-4ef9-988a-d784cc72f258 
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409

Allocating group tables: done 
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

我们必须在某个地方挂载两个文件系统,所以我们将创建两个新目录,/books/projects

root@ubuntu-linux:~# mkdir /books /projects

现在我们可以挂载两个文件系统了:

root@ubuntu-linux:~# mount /dev/mapper/myvg-mybooks /books 
root@ubuntu-linux:~# mount /dev/mapper/myvg-myprojects /projects

我们可以检查mount命令输出的最后两行:

root@ubuntu-linux:~# mount | tail -n 2
/dev/mapper/myvg-mybooks on /books type ext4 (rw,relatime,data=ordered)
/dev/mapper/myvg-myprojects on /projects type ext4 (rw,relatime,data=ordered)

确实!两个文件系统都已挂载。

总结一下;这些是你需要遵循的创建 LVM 逻辑卷的步骤:

  1. 创建一个物理卷。

  2. 创建一个卷组。

  3. 创建一个逻辑卷。

  4. 在逻辑卷上创建一个文件系统。

  5. 挂载文件系统。

相当容易,对吧?

扩展逻辑卷

现在是欣赏的时刻。在你迄今为止所付出的辛苦工作之后,你会明白为什么 LVM 在 Linux 中如此重要。

让我们消耗掉/books中所有可用的空间。注意我们只有 2 GB 可用:

root@ubuntu-linux:~# df -h /books
Filesystem               Size Used Avail Use% Mounted on
/dev/mapper/myvg-mybooks 2.0G 6.0M 1.8G   1% /books

切换到/books目录并创建一个名为book1的 1 GB 文件,如下所示:

root@ubuntu-linux:/books# dd if=/dev/zero of=book1 bs=1G count=1 
1+0 records in
1+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 1.47854 s, 726 MB/s

现在创建另一个大小为 900 MB 的文件book2

root@ubuntu-linux:/books# dd if=/dev/zero of=book2 bs=900M count=1 
1+0 records in
1+0 records out
943718400 bytes (944 MB, 900 MiB) copied, 1.34533 s, 701 MB/s

我们现在的磁盘空间不够了!如果你尝试创建一个 100 MB 的文件,你会收到一个错误:

root@ubuntu-linux:/books# dd if=/dev/zero of=book3 bs=100M count=1 dd: error writing 'book3': No space left on device
1+0 records in
0+0 records out
6103040 bytes (6.1 MB, 5.8 MiB) copied, 0.0462688 s, 132 MB/s

我们现在在/books中正式用完了磁盘空间:

root@ubuntu-linux:/books# df -h /books
Filesystem               Size Used Avail Use% Mounted on
/dev/mapper/myvg-mybooks 2.0G 2.0G  0    100% /books

LVM 来拯救我们了。我们的myvg卷组上还有一些磁盘空间,所以我们可以扩展我们的逻辑卷的大小,从而扩展我们的文件系统的大小:

root@ubuntu-linux:/books# vgs
 VG   #PV #LV #SN  Attr VSize VFree 
 myvg 2    2   0 wz--n- 3.99g 1.50g

我们的myvg中确切还剩下 1.5 GB 的磁盘空间。现在我们可以使用lvextend命令向我们的/dev/mapper/myvg-mybooks逻辑卷添加 1 GB:

root@ubuntu-linux:/books# lvextend -r --size +1G /dev/mapper/myvg-mybooks
 Size of logical volume myvg/mybooks changed from 2.00 GiB (512 extents) to 
    3.00 GiB (768 extents).
 Logical volume myvg/mybooks successfully resized. 
resize2fs 1.44.1 (24-Mar-2018)
Filesystem at /dev/mapper/myvg-mybooks is mounted on /books; on-line resizing required 
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/mapper/myvg-mybooks is now 786432 (4k) blocks long.

-r选项是必需的,因为它会随着逻辑卷一起调整文件系统的大小。现在我们可以看到我们的mybooks逻辑卷已经从 2 GB 增长到 3 GB:

root@ubuntu-linux:/books# lvs
 LV          VG    Attr LSize     Pool Origin Data% Meta% Move Log Cpy%Sync Convert 
 mybooks    myvg -wi-ao---- 3.00g
 myprojects myvg -wi-ao---- 500.00m

因此,我们在/books中获得了更多的磁盘空间:

root@ubuntu-linux:/books# df -h /books
Filesystem               Size Used Avail Use% Mounted on
/dev/mapper/myvg-mybooks 2.9G 1.9G 865M  70% /books

现在让我们检查一下我们的myvg卷组中还剩下多少磁盘空间:

root@ubuntu-linux:/books# vgs
 VG   #PV #LV #SN Attr   VSize VFree 
 myvg 2    2   0  wz--n- 3.99g 516.00m

让我们全力以赴,将我们的myprojects逻辑卷扩展到myvg中剩下的所有空间:

root@ubuntu-linux:~# lvextend -r -l +100%FREE /dev/mapper/myvg-myprojects
 Size of logical volume myvg/myprojects changed from 516.00 MiB (129 extents) 
    to 1016.00 MiB (254 extents).
 Logical volume myvg/myprojects successfully resized. 
resize2fs 1.44.1 (24-Mar-2018)
Filesystem at /dev/mapper/myvg-myprojects is mounted on /projects;
The filesystem on /dev/mapper/myvg-myprojects is now 1040384 (1k) blocks long

注意我们的myprojects逻辑卷的大小已经增加,并且已经占用了myvg中剩下的所有空间:

root@ubuntu-linux:~# lvs
 LV          VG    Attr     LSize    Pool Origin Data% Meta% Move Log Cpy%Sync Convert 
 mybooks    myvg -wi-ao---- 3.00g
 myprojects myvg -wi-ao---- 1016.00m 
root@ubuntu-linux:~# vgs
 VG   #PV #LV #SN Attr   VSize VFree 
 myvg 2    2   0  wz--n- 3.99g 0

现在我们无法扩展我们的逻辑卷,因为myvg卷组的空间用完了。尝试向我们的mybooks逻辑卷添加 12 MB,你会收到一个错误消息:

root@ubuntu-linux:~# lvextend -r --size +12M /dev/mapper/myvg-mybooks 
 Insufficient free space: 3 extents needed, but only 0 available

扩展卷组

只有在卷组上有可用空间时,我们才能扩展我们的逻辑卷。那么如何扩展卷组呢?我们只需向其中添加一个物理卷即可!

记住,我漏掉了一个物理卷/dev/sdb4,我没有将其添加到卷组myvg中。现在是时候添加它了!

要扩展卷组,我们使用vgextend命令,后面跟着卷组名称,然后是你希望添加的物理卷。所以要将物理卷dev/sdb4添加到我们的myvg卷组中,你可以运行以下命令:

root@ubuntu-linux:~# vgextend myvg /dev/sdb4 
 Volume group "myvg" successfully extended

现在我们已经向我们的myvg卷组中添加了整整 2 GB:

root@ubuntu-linux:~# vgs
 VG   #PV #LV #SN Attr    VSize VFree 
 myvg 3    2   0  wz--n- <5.99g <2.00g

这是多么令人惊奇啊?现在你可以扩展你的两个逻辑卷中的任意一个,因为我们向卷组添加了更多的磁盘空间。我们都应该花点时间来欣赏 Linux LVM 的强大和灵活性。

现在是本书中最后一个知识检查练习的时候了。我相信你会想念它们的!

知识检查

对于接下来的练习,打开你的终端并尝试解决以下任务:

  1. 向你的虚拟机添加一个新的 1 GB 磁盘。

  2. 在你的新磁盘上创建三个大小为 250 MB 的分区。

  3. 使用你的三个新分区创建三个物理卷。

  4. 创建一个名为bigvg的卷组,跨越你的三个物理卷。

  5. 创建一个名为biglv的大小为 500 MB 的逻辑卷。

  6. biglv逻辑卷上创建一个ext4文件系统。

  7. 将你的文件系统挂载到/mnt/wikileaks目录。

echo “再见,我的朋友”

我要祝贺你完成了阅读这本书,并学习了 116 多个 Linux 命令。我希望你和我写作一样享受阅读!Linux 确实需要一个好奇的大脑,我敢打赌你很勇敢地尝试了 Linux。我曾经读过一句话,说“Linux 很友好 - 它只是对它的朋友挑剔”,所以现在我欢迎你加入精英 Linux 俱乐部。

第二十二章:接下来去哪里?

你现在可能会想,“接下来该怎么办?”; 大多数人在学会一项新技能后都会问同样的问题,以下是我对学会 Linux 后该做什么的建议:

  • 将你的新技能投入实践!如果你不继续练习所学的东西,最终会失去它。

  • 验证你的技能!雇主肯定会喜欢你有 Linux 认证,比如 Linux 基金会 LFCS/LFCE 认证或红帽 RHCSA/RHCE 认证。

  • 赚钱!Linux 需求量很大;开始申请 Linux 工作。

  • 成为 Linux 内核开发者!如果你是程序员,或者想成为一名程序员,你可能会考虑学习 Linux 内核,也许有一天你可以成为 Linux 内核的贡献者。

  • 学习另一项技能!你现在已经学会了 Linux;你可能想学习云计算、网络安全或计算机网络。这取决于你的最终目标和兴趣领域。

保持联系

你可以在 LinkedIn 上联系我。此外,如果你想参加我的 Udemy 课程,不要犹豫给我发邮件;我将非常乐意给你发送免费优惠券!

评估

第二十三章:知识检查 1

  1. cal 2023

  2. 免费-h

  3. ls /home/elliot

  4. passwd

  5. echo "Mr.Robot is an awesome TV show!"

真或假

知识检查 2

  1. ls -l /var/log

  2. cat /etc/hostname

  3. touch file1 file2 file3

  4. ls -a /home/elliot

  5. mkdir /home/elliot/fsociety

真或假

知识检查 3

  1. 头-n 2 facts.txt

  2. tail -n 1 facts.txt

  3. tac facts.txt

  4. vi facts.txt

  5. :q

知识检查 4

  1. touch hacker1 hacker2 hacker3

  2. mkdir Linux Windows Mac

  3. touch Linux/cool

  4. touch Windows/boring

  5. touch Mac/expensive

  6. cp hacker1 hacker2 /tmp

  7. cp -r Windows Mac/tmp

  8. mv hacker3 /tmp

  9. mv Linux /tmp

  10. rm Mac/expensive

  11. rmdir Mac

  12. rm -r Windows

  13. rm hacker2

  14. mv hacker1 hacker01

真或假

知识检查 5

  1. 类型 echo

  2. which uptime

  3. whatis mkdir

  4. man mv

  5. apropos calendar

  6. 帮助历史

真或假

知识检查 6

  1. ls -id /var/log

  2. stat /boot

  3. mkdir coins

  4. ln -s coins currency

  5. touch coins/silver coins/gold

  6. touch currency/bronze

  7. ls coins currency

  8. ln 饮料 饮料

  9. rm 饮料

  10. cat drinks

真或假

知识检查 7

  1. su root

  2. passwd root

  3. su - elliot

  4. su

真或假

知识检查 8

  1. useradd -u 333 abraham

  2. groupadd admins

  3. usermod -aG admins abraham

  4. chgrp admins /home/abraham

  5. chmod g=r /home/abraham

真或假

知识检查 9

  1. 头-n 5 facts.txt *|* 尾-n 1

  2. free > system.txt

  3. lscpu >> system.txt

  4. rmdir /var 2> error.txt

知识检查 10

  1. du -b /etc/hostname

  2. cut -d: -f1 /etc/group

  3. wc -l /etc/services

  4. grep bash /etc/passwd

  5. uptime *|* tr [:lower:] [:upper:]

知识检查 11

  1. locate boot.log

  2. find / -size +50M

  3. find / -size +70M -size -100M

  4. find / -user smurf

  5. find / -group developers

知识检查 12

  1. apt-get install tmux

  2. apt-cache depends vim

  3. apt-get install cowsay

  4. apt-get purge cowsay

  5. apt-get update 然后运行 apt-get upgrade

知识检查 13

  1. pgrep terminal

  2. ps -fp pid_of_terminal

  3. 杀-9 pid_of_terminal

  4. firefox &

  5. renice -n -20 pid_of_firefox

知识检查 14

  1. smurf ALL=(ALL) /sbin/fdisk

  2. %developers ALL=(ALL) /usr/bin/apt-get

  3. sudo-lU smurf

知识检查 15

  1. hostnamectl set-hostname darkarmy

  2. netstat -rn 或 ip 路由

  3. traceroute www.ubuntu.com

  4. cat /etc/resolv.conf

  5. nslookup www.distrowatch.com

  6. ifconfig eth0 down

  7. ifconfig eth0 up

知识检查 16

  1. #!/bin/bash
     cal
    
  2. #!/bin/bash
     cal $1
    
  3. #!/bin/bash
     for i in {2000..2020}; do
            cal $i
     done
    

知识检查 17

  1. */10 *   *   *   *   echo "10 minutes have passed!" >> /root/minutes.txt

  2. 0 1  25  12  *   echo "Merry Christmas!" >> /root/holidays.txt

知识检查 18

  1. tar -cvf /root/var.tar.gz /var

  2. tar -jvf /root/tmp.tar.bz2 /tmp

  3. tar -Jvf /root/etc.tar.xz /etc

知识检查 19

  1. 别名 ins=”apt-get install”

  2. 别名 packages=”dpkg -l”

  3. 添加这行

别名 clean="rm -r /tmp/*"

.bashrc文件的末尾。

知识检查 20

  1. 转到虚拟机设置>存储>创建新磁盘。

  2. fdisk /dev/sdc

  3. pvcreate /dev/sdc1 /dev/sdc2 /dev/sdc3

  4. vgcreate bigvg /dev/sdc1 /dev/sdc2 /dev/sdc3

  5. lvcreate -n **biglv** -L 500M **bigvg**

  6. mkfs -t **ext4** /dev/mapper/bigvg-biglv

  7. mount **/dev/mapper/bigvg-biglv** /mnt/wikileaks

posted @ 2024-05-16 19:41  绝不原创的飞龙  阅读(40)  评论(0编辑  收藏  举报