Linux-快速学习手册-全-
Linux 快速学习手册(全)
原文:
zh.annas-archive.org/md5/d44a95bd11f73f80156880d7ba808e3a
译者:飞龙
前言
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 上,您所做的大部分工作都与文件有关!在本章中,您将学习如何使用流行的文本编辑器,如nano
和vi
来查看和编辑 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 句柄。例如:"exit
和cd
命令是 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 技术,比如汽车级 Linux(AGL)。您可以在www.automotivelinux.org上找到更多信息。
Linux 还运行在许多嵌入式设备上,并且是流行的树莓派、Beagle Bone 和许多其他微控制器的基础。您甚至可能会惊讶地知道一些洗衣机也在运行 Linux!所以每当你去洗衣服的时候,花点时间,感谢我们生活中有 Linux。
安装 Linux 虚拟机
有多种安装 Linux 系统的方法。例如,如果您目前正在作为主要操作系统运行 Windows,那么您可能可以在 Windows 旁边双启动 Linux,但这种方法对初学者不友好。安装过程中的任何错误可能会给您带来很多头痛,而且在某些情况下,您甚至可能无法再启动 Windows!我想要帮您避免很多痛苦和烦恼,所以我将向您展示如何将 Linux 安装为虚拟机。
什么是虚拟机?
虚拟机就是在另一台计算机(主机)内运行的计算机。虚拟机共享主机资源,表现得就像独立的物理机一样。
您还可以拥有嵌套虚拟机,这意味着您可以在另一个虚拟机内运行虚拟机。
安装虚拟机的过程很简单,您只需要按照以下步骤进行:
-
安装 VirtualBox(或 VMware Player)。
-
下载任何 Linux 发行版的 ISO 镜像。
-
打开 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:选择名称和类型
之后,点击“继续”,选择要为虚拟机分配多少内存。我强烈建议选择2
GB 或更多。例如,在下面的截图中,我选择为我的虚拟机分配4096
MB 的内存(RAM),相当于4
GB。
图 4:选择内存大小
之后,点击“继续”,确保选择“现在创建虚拟硬盘”,如下截图所示,然后点击“创建”。
图 5:创建硬盘
之后,选择VDI(VirtualBox 磁盘映像),如下截图所示,然后点击继续。
图 6:硬盘文件类型
现在选择“动态分配”,如下截图所示,然后点击“继续”。
图 7:物理硬盘上的存储
现在您可以选择虚拟机的硬盘大小。我强烈建议您选择10
GB 或更高。在下面的截图中,我选择了20
GB 作为我的虚拟机的硬盘大小。
图 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 --+
:过去十五分钟的负载平均值。
从负载平均值的定义中,我们可以得出以下关键点:
-
负载平均值为
0.0
表示系统处于空闲状态(什么也不做)。 -
如果 1 分钟负载平均值高于
5
或15
分钟的平均值,则这意味着您的系统负载正在增加。 -
如果 1 分钟负载平均值低于
5
或15
分钟的平均值,则这意味着您的系统负载正在减少。
例如,负载平均值为:
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 命令变得更容易。
恭喜!您已经完成了第一章。现在是您的第一个知识检查练习的时间。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
显示 2023 年的整个日历。
-
以人类可读的格式显示系统的内存信息。
-
显示您的主目录的内容。
-
更改当前用户密码。
-
在您的终端上打印出“Mr. Robot 是一部很棒的电视节目!”
正确还是错误
-
DATE
命令显示当前日期和时间。 -
重新启动您的 Linux 系统,只需运行
restart
命令。 -
运行
free -h
和free --human
命令之间没有区别。 -
如果您的平均负载值递增,系统负载随时间增加。
load average: 2.12, 3.09, 4.03
- 如果您的平均负载值是递减的,系统负载随时间减少。
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
图 4和5可以帮助您可视化。您现在在/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 的内容
父目录和当前目录
在文件系统的每个目录下都有两个特殊的目录:
-
当前工作目录用一个点(
.
)表示 -
父目录用两个点(
..
)表示
图 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 3
:ls -a -l /home/elliot
的最后两列
你已经知道输出的最后一列(Table 3
的第二列)显示文件名,但是前一列(Table 3
的第一列)显示的所有这些日期是什么呢?
Table 3
的第一列中的日期表示每个文件的最后修改时间,即文件被修改(编辑)的最后时间。
你可以使用 touch
命令更改文件的修改时间。
为了演示,让我们首先获取 elliot
的 Desktop
目录的修改时间,你可以通过运行 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
命令做两件事:
-
你可以更新现有文件的最后修改和访问时间。
-
你可以创建新的空文件。
touch
命令只能创建常规文件;它不能创建目录。另外,请注意它更新修改和访问时间,那么有什么区别呢?
-
修改时间 > 文件最后一次被更改或修改的时间。
-
访问时间 > 文件最后一次被访问(读取)的时间。
默认情况下,touch
命令会同时更改文件的修改和访问时间。我在 elliot
的主目录中创建了三个文件:file1
、file2
和 file3
:
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
命令在桌面上创建三个目录-Music
,Movies
和Books
:
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
,并在其中创建了三个目录-dir5
,dir6
和dir7
。
组合命令选项
您已经学会了许多可以与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/
相当酷!好的,这就是本章的结束,现在是时候进行可爱的知识检查了。
知识检查
对于以下练习,打开终端并尝试解决以下任务:
-
对
/var/log
中的所有文件进行长列表。 -
显示文件
/etc/hostname
的内容。 -
在
/home/elliot
中创建三个文件-file1
,file2
和file3
。 -
列出
elliot
的主目录中的所有文件(包括隐藏文件)。 -
在
/home/elliot
中创建一个名为fsociety
的目录。
真或假
-
/home/root
是 root 用户的主目录。 -
dir1/dir2/dir3
是绝对路径的一个例子。 -
/home/elliot/Desktop
是绝对路径的一个例子。 -
touch -m file1
将更新file1
的访问时间。 -
mkdir dir1 dir2 dir3
将创建三个目录-dir1
,dir2
和dir3
。
见编辑器
首先,让我告诉您一些可能会让您感到惊讶的事情。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
编辑器以两种不同的模式工作:
-
插入
模式 -
命令
模式
插入
模式使您能够在文件中插入文本。另一方面,命令
模式允许您执行复制、粘贴和删除文本等操作。命令
模式还允许您搜索和替换文本以及许多其他操作。
插入模式
默认情况下,您首次打开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 (与:wq 或ZZ 相同)。 |
:q |
不保存退出vi 。 |
:q! |
强制退出vi 而不保存。 |
表 8:保存和退出 vi
所以让我们保存文件并退出vi
编辑器。当然,您可以使用以下任何命令:
-
:wq
-
:x
-
ZZ
它们都实现了相同的结果,即保存并退出vi
。
图 21:保存并退出 vi
如果您成功退出了vi
编辑器,我要祝贺您,因为您是精英中的一员。互联网上有数百个关于一些人打开vi
编辑器后从未能退出的模因和漫画!
文件查看命令
在某些情况下,您可能只想查看文件而不编辑它。虽然您仍然可以使用文本编辑器如nano
或vi
来查看文件,但在 Linux 中有更快的查看文件的方法。
cat 命令
cat
命令是 Linux 中最受欢迎和经常使用的命令之一。cat
(concatenate的缩写)命令将文件连接并打印到标准输出(终端)。
要查看我们创建的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
命令不仅可以查看文件,还可以连接(放在一起)文件。为了演示,使用您喜欢的文本编辑器创建以下三个文件:
-
file1.txt
(插入行“第一个文件”) -
file2.txt
(插入行“第二个文件”) -
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.txt
和file2.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源于less
比more
提供更多的想法。
less
命令是另一个分页程序,就像more
一样;它允许你一次查看一个页面的文本文件。less
的优点是你可以使用上/下箭头键在文件中导航。此外,less
比more
更快。
你可以通过运行以下命令使用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.
你知道现在是几点吗?是时候进行一些知识检查了。
知识检查
对于以下练习,打开你的终端并尝试解决以下任务:
-
只查看文件
facts.txt
的前两行。 -
只查看文件
facts.txt
的最后一行。 -
以相反的顺序显示文件
facts.txt
的内容。 -
使用
vi
编辑器打开文件facts.txt
。 -
退出
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.txt
、banana.txt
和carrot.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
中创建三个文件paris
、tokyo
和london
,如下所示:
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 的主目录中创建三个目录d1
、d2
和d3
:
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.txt
、banana.txt
和carrot.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.txt
、banana.txt
和carrot.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
现在您可以将三个目录d1
、d2
和cities
移动到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
命令的工作原理:
-
如果目标目录存在,
mv
命令将移动源文件到目标目录。 -
如果目标目录不存在,
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.txt
,banana.txt
和carrot.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.txt
和a2.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
命令成功删除了它。
没有什么比一个好的知识检查练习更能让信息牢固地留在你的脑海中了。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
在您的主目录中创建三个文件
hacker1
,hacker2
和hacker3
。 -
在您的主目录中创建三个目录
Linux
,Windows
和Mac
。 -
在您在任务 2 中创建的
Linux
目录中创建一个名为cool
的文件。 -
在您在任务 2 中创建的
Windows
目录中创建一个名为boring
的文件。 -
在您在任务 2 中创建的
Mac
目录中创建一个名为expensive
的文件。 -
将两个文件
hacker1
和hacker2
复制到/tmp
目录。 -
将两个目录
Windows
和Mac
复制到/tmp
目录。 -
将文件
hacker3
移动到/tmp
目录。 -
将目录
Linux
移动到/tmp
目录。 -
从您的主目录中的
Mac
目录中删除文件expensive
。 -
从您的主目录中删除目录
Mac
。 -
从您的主目录中删除目录
Windows
。 -
从您的主目录中删除文件
hacker2
。 -
将文件
hacker1
重命名为hacker01
。
真或假
-
cp
命令可以复制目录,而不使用递归选项-r
。 -
在移动目录时,您必须使用递归选项
-r
。 -
您可以使用
mv
命令来重命名文件或目录。 -
您可以使用
rmdir
命令删除非空目录。 -
您可以使用
rm -r
命令删除非空目录。
阅读你的手册!
你现在可能会对自己说:“Linux 太难了!有很多命令,甚至有更多的命令选项!我不可能掌握所有这些命令并记住它们。”如果这是你的想法,相信我,你是聪明的。记住所有存在的 Linux 命令是不可能的,即使是最有经验的 Linux 管理员也永远不可能记住所有命令,甚至连 Linus Torvalds 本人也不可能!
那么等等?如果是这样,那么解决方案是什么呢?答案就在美丽的 Linux 文档世界中。Linux 有非常完善的文档,以至于很难在其中迷失。Linux 中有各种工具,不仅可以帮助你记住命令,还可以帮助你理解如何使用它们。
在我的职业生涯中遇到了许多 Linux 专业人士,我注意到最熟练的 Linux 管理员不是那些记住了所有命令的人,而是那些知道如何充分利用 Linux 文档的人。女士们先生们,我强烈建议你系好安全带,仔细阅读本章。我向你保证,你心中的恐惧很快就会消失!
第六章:Linux 命令的四个类别
所有 Linux 命令必须属于以下四个类别中的一个:
-
可执行程序:通常是用 C 编程语言编写的。
cp
命令就是一个可执行命令的例子。 -
别名:基本上是命令(或一组命令)的另一个名称。
-
shell 内置命令:shell 也支持内部命令。
exit
和cd
命令就是 shell 内置命令的两个例子。 -
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
页面。例如,cd
或exit
命令没有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 有各种有用的工具可供你使用;所以确保你充分利用它们!
知识检测
对于以下练习,打开你的终端并尝试解决以下任务:
-
你需要知道
echo
命令是一个 shell 内置命令还是可执行程序,你会运行哪个命令? -
显示
uptime
命令可执行文件的位置。 -
显示
mkdir
命令的简要描述。 -
你忘记了如何使用
mv
命令,你打算怎么办? -
你忘记了用来显示日历的命令,你打算怎么办?
-
history
命令是一个 shell 内置命令,因此它没有 man 页面。你想要清除你的历史记录,但不知道该怎么做。你打算怎么办?
真或假
-
whereis
命令用于定位命令。 -
你可以互换使用
man -p
和apropos
。 -
你可以使用
whatis
命令来获取一个命令的简要描述。 -
你可以使用
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 编号:
-
ls -i
文件 -
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
中创建三个文件-swimming
,soccer
和hockey
,如下所示:
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
我告诉过你,目录不允许有硬链接!你为什么怀疑我?
令人震惊的事实
没有办法区分原始文件和硬链接。例如,如果给你两个文件,其中一个恰好是另一个文件的硬链接,那么没有办法知道哪个文件是原始文件!这就像鸡和蛋的困境;没有人知道哪个先出现!
知识检测
对于以下练习,打开你的终端并尝试解决以下任务:
-
显示
/var/log
目录的 inode 编号。 -
显示
/boot
目录的硬链接数。 -
在你的主目录中创建一个名为
coins
的新目录。 -
创建一个指向
coins
的软链接,名为currency
。 -
在
coins
目录中,创建两个文件——silver
和gold
。 -
在
currency
目录中创建一个新文件bronze
。 -
列出
coins
和currency
两个目录的内容。 -
在你的主目录中创建一个包含“咖啡很棒”的新文件
beverages
,并创建一个名为drinks
的硬链接指向beverages
。 -
在
drinks
文件中添加一行“柠檬很清爽”,然后删除beverages
文件。 -
显示你的
drinks
文件的内容。
真或假
-
文件名是 inode 数据结构的一部分。
-
文件大小是 inode 数据结构的一部分。
-
你可以创建指向目录的软链接。
-
你可以创建指向目录的硬链接。
-
目录的最小硬链接数为
2
。 -
软链接与原始文件具有相同的 inode 编号。
-
硬链接与原始文件具有相同的 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!”
知识检查
对于以下练习,打开你的终端并尝试解决以下任务:
-
切换到
root
用户。 -
更改
root
用户的密码。 -
切换到用户
elliot
并登陆到/home/elliot
。 -
现在切换到 root 用户,但保留当前工作目录
/home/elliot
。
真或假
-
root
用户是 Linux 中最强大的用户。 -
使用
su
命令而不指定用户名将切换到 root 用户。 -
我们使用
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
太棒了!我们现在已经创建了两个用户:tom
和jerry
。现在,让我们切换到用户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 tom
将tom
的 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
的主目录。系统中创建的每个新用户现在都将有一个很酷的问候消息!请注意,像tom
和jerry
这样的旧用户不会在他们的主目录中有文件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:
而且它看起来就像我们期望的那样。很酷!
添加组成员
用户tom
和jerry
都是卡通人物,因此将它们都添加到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:
正如您所看到的,tom
和jerry
现在都列为cartoon
组的成员。
您可以使用id
命令查看系统上任何用户的组成员资格。例如,如果您想要检查tom
属于哪些组,可以运行命令id tom
:
root@ubuntu-linux:~# id tom
uid=444(tom) gid=1007(tom) groups=1007(tom),1009(cartoon)
让我们通过创建三个新用户sara
,peter
和rachel
来进行更多练习:
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
组现在有三个成员-sara
,peter
和rachel
。但是有一些奇怪的地方!看起来当我们创建用户sara
,peter
和rachel
时,它也创建了它们作为组!但是为什么会发生这种情况呢?好吧,让我在下一节中向您解释。
主要与次要组
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
是两个组dummy
和cartoon
的成员。但是,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
是两个次要组cartoon
和developers
的成员。
好了!够了这些虚拟的东西。让我们删除用户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
中创建和删除文件,因为他有写入权限。
使用八进制表示法
您可以使用数字4
,2
和1
来设置文件权限,而不是字母r
,w
和x
。看一下下面的图片:
图 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:八进制表示法与字面表示法
这一章有点冗长。休息一下,然后回来完成知识检测练习!
知识检测
对于以下练习,打开你的终端并尝试解决以下任务:
-
创建一个用户
abraham
,用户 ID 为333
。 -
创建一个新的组
admins
。 -
将用户
abraham
添加到admins
组。 -
将
admins
设为目录/home/abraham
的组所有者。 -
admins
组的成员只能列出目录/home/abraham
的内容。
真或假
-
chmod a=rxw facts.txt
将会得到与chmod 777 facts.txt
相同的结果。 -
chmod a=rw facts.txt
将会得到与chmod 665 facts.txt
相同的结果。 -
用户
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
您可以看到它包括stdout
和stderr
。
丢弃输出
有时候你不需要将输出重定向到任何地方;你只是想抛弃它并摆脱它。在这种情况下,你可以将输出重定向到/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
相同的内容。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
仅显示文件
facts.txt
的第 5 行。 -
将
free
命令的输出保存到名为system.txt
的文件中。 -
将
lscpu
命令的输出追加到文件system.txt
中。 -
运行命令
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
个英文字符的代码,每个字母被分配一个从0
到127
的数字。
你的计算机不理解人类语言(字母),只理解数字!因此,英语语言中的每个字符都被转换为一个数字。你的计算机将任何文本文件都视为一堆堆的数字!
现在让我们创建一个名为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
awk
比cut
有一个优势,那就是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] 将匹配字符a 、b 或c 。 |
[!characters] |
匹配不属于字符集的任何字符。基本上是[characters] 的否定。例如,[!abc] 将匹配任何不是a 、b 或c 的字符。 |
[[: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
您还可以同时使用多个通配符。例如,如果您只想列出以字母a
或f
开头的文件名,您可以使用[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] 将匹配字符 a ,b 或 c 。 |
[^characters] |
匹配不属于字符集字符的任何字符。基本上是对 [characters] 的否定。例如,[!abc] 将匹配不是 a ,b 或 c 的任何字符。 |
{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 页面包括对本章讨论的所有正则表达式的解释。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
显示文件
/etc/hostname
的大小(以字节为单位)。 -
仅显示文件
/etc/group
中的组名。 -
显示文件
/etc/services
中的总行数。 -
仅显示文件
/etc/passwd
中包含单词 "bash" 的行。 -
显示
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.txt
和LARGE.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.txt
和LARGE.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
页面,以探索可以使用的众多其他选项。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
使用
locate
命令找到文件boot.log
的路径。 -
查找所有大小大于
50
MB 的文件。 -
查找所有大小在
70
MB 和100
MB 之间的文件。 -
查找所有属于用户
smurf
的文件。 -
查找所有属于组
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.
您还可以通过运行以下命令来升级系统上安装的所有软件包:
-
apt-get update
-
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 术语中,升级系统上安装的所有软件包的过程称为打补丁系统。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
在您的系统上安装
tmux
软件包。 -
列出
vim
软件包的所有依赖项。 -
在您的系统上安装
cowsay
软件包。 -
删除
cowsay
软件包以及其所有配置文件。 -
升级系统上的所有软件包(打补丁您的系统)。
杀死进程
在您的系统上运行的任何程序都是一个进程。在本章中,您将学习有关 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
注意到每个信号都有一个数字值。例如,19
是SIGSTOP
信号的数字值。
为了了解信号的工作原理,让我们首先将 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
处理进程优先级
每个进程都有一个由友好度量表确定的优先级,范围从-20到19。友好值越低,进程的优先级越高,所以友好值为-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 命令
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
列出您正在运行的终端的进程 ID。
-
列出您正在运行的终端的父进程 ID。
-
使用
kill
命令关闭您的终端。 -
将 Firefox 作为后台进程启动。
-
将 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
命令来编辑用户smurf
的sudo
规则:
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
的用户别名,其中包括usersmurf
和bob
,如下所示:
User_Alias MANAGERS = smurf,bob
您可以使用命令别名将多个命令分组在一起。例如,您可以创建一个名为USER_CMDS
的命令别名,其中包括useradd
、userdel
和usermod
命令:
Cmnd_Alias USER_CMDS = /usr/sbin/useradd, /usr/sbin/userdel, /usr/sbin/usermod
现在您可以同时使用别名:
MANAGERS ALL=(ALL) USER_CMDS
授予用户smurf
和bob
运行useradd
、userdel
和usermod
命令的权限。
组权限
您还可以在/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
。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
添加一个
sudo
规则,使用户smurf
可以运行fdisk
命令。 -
添加一个
sudo
规则,使developers
组可以运行apt-get
命令。 -
列出用户
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
我的系统上有三个网络接口:
-
eth0
:以太网接口 -
lo
:回环接口 -
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 网络的理解了。
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
将您的主机名更改为
darkarmy
。 -
显示您的默认网关的 IP 地址。
-
从您的计算机到
www.ubuntu.com
的路由跟踪。 -
显示您的 DNS 的 IP 地址。
-
显示
www.distrowatch.com
的 IP 地址。 -
关闭您的以太网接口。
-
重新启动您的以太网接口。
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!
如你所见,现在它完美地运行了!
你也可以使用elif
(else-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
循环
for
和while
循环都会在测试条件为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
的脚本,其中有两个函数add
和sub
:
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
该脚本有两个函数add
和sub
。add
函数计算并输出任意两个数字的总和。另一方面,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!
知识检查
对于以下练习,打开你的终端并尝试解决以下任务:
-
创建一个 bash 脚本,显示当前月份的日历。
-
修改你的脚本,以便显示任何年份(作为参数传递)的日历。
-
修改你的脚本,以便显示从
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 作业将在星期日的每个小时的5
、20
和40
分钟运行:
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
知识检查
对于以下练习,打开您的终端并尝试解决以下任务:
-
为 root 用户创建一个 cron 作业,每 10 分钟运行一次。该 cron 作业将简单地将行“已经过去 10 分钟!”附加到文件
/root/minutes.txt
中。 -
为 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.gz
的gzip
压缩存档,您首先需要切换到/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 上另一种流行的压缩方法。平均而言,bzip2
比gzip
慢;然而,bzip2
在将文件压缩到更小的大小方面做得更好。
你可以使用tar
命令的-j
选项来使用bzip2
压缩压缩存档,如下所示:
tar -cjf compressed_archive archive_name
注意这里唯一的区别是我们使用bzip2
压缩的-j
选项,而不是gzip
压缩的-z
选项。
因此,要将scripts.tar
存档压缩成名为scripts.tar.bz2
的bzip2
压缩存档,你首先需要切换到/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.gz
和bzip2
压缩的存档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.bz2
比gzip
压缩的存档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
注意这里我们使用大写字母J
与xz
压缩。因此,要将scripts.tar
存档压缩成名为scripts.tar.xz
的xz
压缩存档,你首先需要切换到/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 秒!现在让我们测量创建相同目录/boot
的bzip2
压缩存档所需的时间:
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
快得多。现在让我们看看创建相同目录/boot
的xz
压缩存档所需的时间:
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
排名最后。
知识检查
对于以下练习,打开你的终端并尝试解决以下任务:
-
在
/root
中为/var
中的所有文件创建一个名为var.tar.gz
的gzip
存档。 -
在
/root
中为/tmp
中的所有文件创建一个名为tmp.tar.bz2
的bzip2
存档。 -
在
/root
目录中为/etc
目录中的所有文件创建一个名为etc.tar.xz
的xz
归档文件。
创建你自己的命令
有时,你可能会很难记住一个命令。其他时候,你会发现自己一遍又一遍地运行一个非常长的命令,这让你发疯。在本章中,你将学习如何创建自己的命令,因为你才是真正的老板。
第二十章:你的第一个别名
假设你总是忘记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
时,它将同时运行date
和calendar
命令:
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'
创建永久别名
到目前为止,我们一直在创建临时别名;也就是说,我们创建的memory
和date
这两个别名是临时的,只在当前终端会话中有效。一旦关闭终端,这两个别名就会消失。
打开一个新的终端会话,然后尝试运行我们创建的两个别名:
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
现在你可以永远使用你的两个别名memory
和date
,而不必担心在关闭当前终端会话后它们会消失:
删除别名
让我们创建另一个临时别名,名为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 ...
我会让你自己处理这个问题;我就是这么邪恶!哈哈。
知识检查
对于以下练习,打开你的终端并尝试解决以下任务:
-
为
apt-get install
命令创建一个临时别名ins
。 -
为
dpkg -l
命令创建一个临时别名packages
。 -
创建一个名为
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/0
;pts是伪终端的缩写。现在,这个终端由文件/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
)。现在我们不想操作这个磁盘,因为它包含了根文件系统,所以让我们为学习目的向我们的虚拟机添加另一个磁盘。
向虚拟机添加磁盘
要成功向虚拟机添加新磁盘,您需要遵循一些步骤。您必须按照特定顺序执行这些步骤:
-
关闭您的虚拟机。
-
转到虚拟机设置|存储|创建新磁盘。
-
启动您的虚拟机。
因此,第一步非常简单;关闭您的虚拟机,因为在虚拟机仍在运行时无法向其添加新磁盘。对于第二步,您需要进入虚拟机设置,然后点击存储,然后选择您的磁盘控制器,右键单击,然后按图 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 上有许多不同类型的文件系统可用。重要的是要注意,ext4
和xfs
是最常用的文件系统。图 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#
现在,您可以运行以下命令创建一个名为bigGame
的1
GB 文件:
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
的大小为3
GB 的文件:
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
所示。最后,您可以在逻辑卷上创建文件系统。
创建物理卷
创建物理卷的步骤非常简单;您只需要一个磁盘或一个分区。我们已经创建了一个2
GB 的分区/dev/sdb1
。现在继续在/dev/sdb
下创建三个大小为2
GB 的分区。
最终结果应该是这样的:
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
的大小等于4
GB,这是/dev/sdb2
和/dev/sdb3
的总大小。
创建逻辑卷
现在我们可以在我们的mvg
卷组上创建逻辑卷。
要创建逻辑卷,我们使用lvcreate
命令,后面跟着逻辑卷的大小、逻辑卷的名称,最后是卷组名称:
lvcreate --size 2G --name lv_name vg_name
让我们创建一个名为mybooks
的逻辑卷,大小为2
GB:
root@ubuntu-linux:~# lvcreate --size 2G --name mybooks myvg
Logical volume "mybooks" created.
现在创建另一个名为myprojects
的逻辑卷,大小为500
MB:
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 逻辑卷的步骤:
-
创建一个物理卷。
-
创建一个卷组。
-
创建一个逻辑卷。
-
在逻辑卷上创建一个文件系统。
-
挂载文件系统。
相当容易,对吧?
扩展逻辑卷
现在是欣赏的时刻。在你迄今为止所付出的辛苦工作之后,你会明白为什么 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 GB 磁盘。
-
在你的新磁盘上创建三个大小为 250 MB 的分区。
-
使用你的三个新分区创建三个物理卷。
-
创建一个名为
bigvg
的卷组,跨越你的三个物理卷。 -
创建一个名为
biglv
的大小为 500 MB 的逻辑卷。 -
在
biglv
逻辑卷上创建一个ext4
文件系统。 -
将你的文件系统挂载到
/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
-
cal 2023
-
免费-h
-
ls /home/elliot
-
passwd
-
echo "Mr.Robot is an awesome TV show!"
真或假
-
假
-
假
-
真
-
假
-
真
知识检查 2
-
ls -l /var/log
-
cat /etc/hostname
-
touch file1 file2 file3
-
ls -a /home/elliot
-
mkdir /home/elliot/fsociety
真或假
-
假
-
假
-
真
-
假
-
真
知识检查 3
-
头-n 2 facts.txt
-
tail -n 1 facts.txt
-
tac facts.txt
-
vi facts.txt
-
:q
知识检查 4
-
touch hacker1 hacker2 hacker3
-
mkdir Linux Windows Mac
-
touch Linux/cool
-
touch Windows/boring
-
touch Mac/expensive
-
cp hacker1 hacker2 /tmp
-
cp -r Windows Mac/tmp
-
mv hacker3 /tmp
-
mv Linux /tmp
-
rm Mac/expensive
-
rmdir Mac
-
rm -r Windows
-
rm hacker2
-
mv hacker1 hacker01
真或假
-
假
-
假
-
真
-
假
-
真
知识检查 5
-
类型 echo
-
which uptime
-
whatis mkdir
-
man mv
-
apropos calendar
-
帮助历史
真或假
-
假
-
假
-
真
-
真
知识检查 6
-
ls -id /var/log
-
stat /boot
-
mkdir coins
-
ln -s coins currency
-
touch coins/silver coins/gold
-
touch currency/bronze
-
ls coins currency
-
ln 饮料 饮料
-
rm 饮料
-
cat drinks
真或假
-
假
-
真
-
真
-
假
-
真
-
假
-
真
知识检查 7
-
su root
-
passwd root
-
su - elliot
-
su
真或假
-
真
-
真
-
假
知识检查 8
-
useradd -u 333 abraham
-
groupadd admins
-
usermod -aG admins abraham
-
chgrp admins /home/abraham
-
chmod g=r /home/abraham
真或假
-
真
-
假
-
假
知识检查 9
-
头-n 5 facts.txt *|* 尾-n 1
-
free > system.txt
-
lscpu >> system.txt
-
rmdir /var 2> error.txt
知识检查 10
-
du -b /etc/hostname
-
cut -d: -f1 /etc/group
-
wc -l /etc/services
-
grep bash /etc/passwd
-
uptime *|* tr [:lower:] [:upper:]
知识检查 11
-
locate boot.log
-
find / -size +50M
-
find / -size +70M -size -100M
-
find / -user smurf
-
find / -group developers
知识检查 12
-
apt-get install tmux
-
apt-cache depends vim
-
apt-get install cowsay
-
apt-get purge cowsay
-
apt-get update 然后运行 apt-get upgrade
知识检查 13
-
pgrep terminal
-
ps -fp pid_of_terminal
-
杀-9 pid_of_terminal
-
firefox &
-
renice -n -20 pid_of_firefox
知识检查 14
-
smurf ALL=(ALL) /sbin/fdisk
-
%developers ALL=(ALL) /usr/bin/apt-get
-
sudo-lU smurf
知识检查 15
-
hostnamectl set-hostname darkarmy
-
netstat -rn 或 ip 路由
-
traceroute www.ubuntu.com
-
cat /etc/resolv.conf
-
nslookup www.distrowatch.com
-
ifconfig eth0 down
-
ifconfig eth0 up
知识检查 16
-
#!/bin/bash cal
-
#!/bin/bash cal $1
-
#!/bin/bash for i in {2000..2020}; do cal $i done
知识检查 17
-
*/10 * * * * echo "10 minutes have passed!" >> /root/minutes.txt
-
0 1 25 12 * echo "Merry Christmas!" >> /root/holidays.txt
知识检查 18
-
tar -cvf /root/var.tar.gz /var
-
tar -jvf /root/tmp.tar.bz2 /tmp
-
tar -Jvf /root/etc.tar.xz /etc
知识检查 19
-
别名 ins=”apt-get install”
-
别名 packages=”dpkg -l”
-
添加这行
别名 clean="rm -r /tmp/*"
到.bashrc
文件的末尾。
知识检查 20
-
转到虚拟机设置>存储>创建新磁盘。
-
fdisk /dev/sdc
-
pvcreate /dev/sdc1 /dev/sdc2 /dev/sdc3
-
vgcreate bigvg /dev/sdc1 /dev/sdc2 /dev/sdc3
-
lvcreate -n **biglv** -L 500M **bigvg**
-
mkfs -t **ext4** /dev/mapper/bigvg-biglv
-
mount **/dev/mapper/bigvg-biglv** /mnt/wikileaks