Skip to content

Latest commit

 

History

History
278 lines (159 loc) · 43.2 KB

File metadata and controls

278 lines (159 loc) · 43.2 KB

四、使用命令行

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

我们将涵盖以下主题:

  • 基本的 Linux 命令
  • 附加程序
  • 理解过程
  • 信号
  • 使用 Bash Shell 变量
  • Bash shell 脚本

基本的 Linux 命令

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

我们将讨论以下命令:

  • cat
  • sort
  • awk
  • tee
  • tar
  • 其他杂项命令

首先,让我们创建一个更小版本的passwd文件来使用cat命令:

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

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

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

如果在这个文件上使用cat,只会产生垃圾。若要分割具有多个连续空格的文件,请改用awktr命令就像是设定替代模式的轻量级版本或子集。它将字符集一转换为字符集二,从stdin读取并输出到stdout。语法是不言自明的。您可以翻译单个字符和字符范围。字符集类似于 POSIX 正则表达式类;阅读手册了解更多信息。

我们来讨论一下sort命令。sort命令对文本文件进行逐行排序。默认情况下,它会考虑整行进行排序。-u旗只打印出唯一的字段。如果我们取一个有数字而不是字母数字值的文件,默认情况下,sort需要字母数字值,所以数字的排序是错误的或者不自然的。要解决这个问题,请使用-n选项,该选项使用数字进行排序。要从下向上排序值,请使用-r标志。如果需要,也可以影响排序列。sort始终考虑整条线。要解决此问题,请使用-k 2.2选项按第二列排序。还有很多排序选项。请参考手册了解更多信息。

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

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

tee是一个有用的命令,可以用来在文件中存储一个stdout命令,也可以在命令行上打印出来。它有助于记录输出,同时还能看到正在发生的事情。给tee命令一个你想写的文件名作为参数。要压缩单个文件,也就是减小文件大小,使用gzip。要解压缩,使用gunzip命令。

要压缩一个完整的子目录,递归使用tar命令。请注意,f选项必须是最后一个选项,后跟您要创建的归档名称作为第一个参数,然后是您要归档和压缩的目录作为第二个参数。要将归档文件提取到以下任何目录,请使用带有以下标志的tar命令,-C是输出目录。hostname打印出主机名;uptime打印服务器电脑开机多长时间,uname打印内核版本等系统信息。

/etc/redhat-release文件中,你会找到这款 CentOS 7 所基于的红帽企业版。在/prog/meminfo文件中,你会找到内存信息,比如你有多少 RAM。在/proc/cpuinfo中,你可以找到关于你的处理器和内核的信息。free -m打印出有用的内存信息,比如你有多少空闲内存。df打印出可用磁盘空间的信息。du -page打印出当前目录下文件占用的空间。如果配合max-depth=1选项使用,还会得到文件夹内容的摘要。users打印出当前登录系统的所有用户。whoami命令打印当前使用该终端的用户的姓名。

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

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

附加程序

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

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

接下来,让我们安装一些程序,让您对系统有一种生活的看法。这将安装htopiotopiftop。最后,让我们安装一些必要的工具,它们是屏幕、计算器、bclsof。首先介绍一下rsync。每个 Linux 用户都需要知道它,因为它是一个很棒的工具,有很多有用的特性。基本上,rsync是一个文件传输程序,但它不是简单地在一个源和目的地之间复制文件;相反,它会同步它们,这意味着只有当源文件不同于目标 qfile 时,它才会传输文件。这节省了大量的数据开销和时间。我经常使用带有-rav标志的rsync,这是默认的用一组公共参数来冗长递归地复制文件。

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

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

网络工具

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

Nmap(消歧义)

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

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

链接

在这一小节中,我们将学习links——命令行网络浏览器。要使用链接程序打开 DuckDuckGo 搜索网站,请使用以下命令行:链接https://duckduckgo.com/。这将打开链接网页浏览器。上下移动光标,到达 DuckDuckGo 文本搜索栏。现在,您可以像在普通的 DuckDuckGo 网站上一样输入您的搜索词,然后按回车键开始搜索。再次使用向上和向下箭头键跳转到您想要浏览的搜索结果。学习链接导航和快捷方式超出了本书的范围。阅读手册以了解更多信息。按 q 键退出链接,然后按键进入键确认选择。

iotop

要实时查看系统的输入输出或短输入输出带宽使用情况,请键入iotop。iotop 需要从根用户开始。你可以使用iotop,例如,了解你的硬盘读写速度,然后按 q 键退出。阅读iotop的手册部分,了解更多关于它的快捷方式,例如,分类栏。

iftop

让我们了解一下iftop程序,它可以实时查看网络流量和网络带宽使用情况并进行监控。同样,该工具需要从根用户帐户开始。可以看到,网络流量可以用这个工具显示,按 q 键退出程序。阅读iftop手册部分,了解更多快捷方式。

快上来

现在,让我们开始htop,它类似于著名的顶级程序,以交互方式查看流程。htop是普通 top 程序的改进版本,它增加了垂直和水平滚动等新功能,这样您就可以看到系统上运行的所有进程以及完整的命令行。htop程序向您展示了许多关于您的系统的不同信息。按下 q 键退出程序。有很多不同的捷径选择可以学习;阅读手册以了解更多信息。

lsof

要打印出所有打开文件的列表,这意味着程序正在访问文件,使用lsof命令。你会得到一个长长的清单;最好用它搭配grep来过滤内容。要在命令行上快速进行一些数学计算,请使用电脑计算器。screen是一个非常有用的命令,可以从 SSH 连接中分离,而无需实际断开或退出,这对于暂停您的工作并稍后返回到您离开的确切位置,或者从另一台计算机上工作非常有用。这可以节省大量时间。首先,要创建新的可拆卸会话,请键入screen。现在做你的工作,例如,在 VI 中键入一个文本。现在,想象你一天的工作结束了,你回家了。没有屏幕,您现在需要保存您的更改,关闭虚拟仪器,并从服务器注销。有了屏幕,只需使用组合键Ctrl+A+D即可脱离当前 SSH 会话。如果您已成功脱离会话,将出现一行显示detached from,然后是屏幕会话标识。现在,为了证明我们可以重新连接到该会话,只需从服务器注销,然后重新登录到服务器。然后,回到服务器类型屏幕列表,获取所有分离屏幕的列表。要重新连接到您的屏幕,请使用屏幕标识:$ screen -r 23433.pts-l_localhost。正如你所看到的,我们完全回到了我们停止的地方。如果您想停止屏幕会话,请键入exit。在这里,我们向您展示了这些程序最基本的用例。

理解过程

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

Linux 系统中的所有进程都是由另一个进程创建的,因此每个进程都有一个创建它的父进程。只有第一个进程没有父进程,在 CentOS 7 中是systemd进程。要获得所有运行进程的列表,运行ps命令。在这里,我们将它与-ev选项一起使用,并将其输出导入less命令,因为它不适合屏幕。您将看到每个进程都有一个唯一的标识符,它被称为进程标识符,简称 PID。第一个过程,系统化过程,PID 为 1。接下来的几个是按顺序递增的。每个进程都有一个关联的用户标识,并且每个进程都有一个由父进程标识列表示的父进程。您会注意到列表中的前两个进程的父进程 PID 为 0,这意味着它们没有父进程。

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

正如我们在上一节中了解到的,您还可以使用tophtop命令来获得系统中进程的动态或实时视图。状态列显示进程的状态,r代表运行,s代表睡眠,以此类推。如果创建了一个新的进程,父进程将被完全克隆或复制到子进程,因此它具有与父进程完全相同的数据和环境。只有 PID 会不同,但是父进程和子进程是完全独立的。

克隆

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

信号

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

使用kill -l获取一个可以发送到进程的所有可用信号的列表。例如,发送给程序杀死它的标准信号是SIGKILL信号,其信号 ID 为 9。所以,让我们先创建一个新的流程,然后杀死它;例如,在后台启动一个新的睡眠过程。正如您已经了解的,将流程放入后台会打印出流程标识。大多数时候,我们使用kill命令来终止系统进程,这些进程通常不是由我们的用户启动的。因此,一个标准的检索方式是使用ps选项,aux,然后按照您想要杀死的进程的名称进行过滤。将ps与选项aux一起使用会打印出完整的命令行,这通常有助于区分正确的进程,因为在此列表中经常有多个进程具有相同的命令名称。在我们的例子中,我们只有一个睡眠进程在运行,我们可以确认正确的进程标识。现在,为了杀死这个进程,使用kill -9发送SIGKILL信号,然后发送进程标识。让我们再次使用ps命令确认这一点:

如你所见,sleep命令已经成功击杀。在上一节中,我们使用了带有百分比作业标识的kill命令,但是使用带有 PID 而不是作业标识的kill命令有什么区别呢?后台和挂起的进程通常通过作业号或作业标识来操作。该编号不同于进程标识,使用它是因为它更短。使用 PID 杀死进程最常用于使用根帐户杀死有故障的系统进程。此外,一个作业可以由多个连续或同时并行运行的进程组成。使用作业标识比跟踪单个流程更容易。

障碍

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

使用 Bash Shell 变量

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

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

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

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

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

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

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

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

Bash shell 脚本介绍

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

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

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

第一个字是函数名,后面跟着左右括号,用来定义一个函数,后面跟着一个花括号;属于一个函数的所有命令都在左括号和右括号中定义,这也称为函数体。函数可以有参数作为普通命令,函数体可以从外部访问这些参数。要访问函数中的某个参数,请使用美元数字符号。所以$1是第一个参数,$2是第二个参数,以此类推。让我们来看看我们的say_hello功能。如果我们用一个参数调用这个函数,这个函数就会用一个参数来执行,这个参数会取在函数体中,我们可以用$1变量来访问第一个参数,这只不过是一个正常的 shell 扩展。

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

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

类似于使用函数,我们也可以在 shell 脚本中访问命令行参数,比如$ vi /tmp/new-script.sh。第一个参数可以使用$1访问,第二个参数$2可以使用等等。在 shell 脚本中,您也可以使用$0访问 shell 脚本的名称。使用$#可以访问参数的总数。例如,要检查脚本是否至少需要两个参数,请执行以下操作:

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

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

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

实现 Bash Shell 脚本

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

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

在这个例子中,我们发出ls命令查看oiip主目录的内容。我们将ls命令的退出状态存储在EXIT Bash 变量中。在下一行,我们现在陈述 if 条件。这可以理解为:ifBash 变量EXIT等于0,然后打印出两行文字,这个if条件用反 if 字,fi。如您所见,两行已经打印出来,这意味着 if 条件为真,因此退出值为0。需要注意的是,您必须非常小心地设置空格和新行,就像我在前面的示例中所做的那样,但是您也可以将完整的 if 语句放在一行中,如果您按下向上箭头键来显示历史记录中的最后一个命令,您就可以看到这一点。如您所见,shell 内部使用分号而不是新行来分隔大多数表达式,这有点难以理解,尤其是在您编写更复杂的 Bash shell 脚本单行的情况下。要否定任何 if 表达式,即如果条件不满足,则if语句的计算结果为真,请使用以下公式:

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

在本例中,if 条件可以理解为:如果退出值不等于0,则打印出文本。在我们的例子中,这是真的,因为退出值是1。If 条件可以包括很多不同的测试,这里无法演示。在这里,遵循最重要的。要测试平等,使用我们刚刚看到的-eq测试。你可以用它来表示数字。对于字符串比较,请改用==运算符。例如,您也可以使用上一节中介绍的逻辑andor表达式来测试替代方案。这个例子可以理解为:如果密码等于Hello_my_world_555或者如果密码等于my_secret_pass。在本例中,密码是正确的。还可以使用等号运算符使用正则表达式。这个语句可以理解为:如果字符串在行首匹配,则 if 条件为 true,其中前两个字符是变量,但之后必须有一个rem,这是真的。对于数值,也可以用-lt-gt代替-eq测试小于或大于的数字,例如测试小于或测试大于。

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

要了解有关所有现有文件测试以及所有可用比较运算符的更多信息,请打开 Bash 手册并搜索条件表达式。我们刚刚学习的最简单的 if 语句的一般语法是,如果条件为真,则执行开头 if 和结尾fi之间的所有命令。现在,您还可以合并一个else分支,如果条件不为真,将执行该分支。下面的截图显示了执行情况:

else 分支由else关键字引入。在我们的示例中,if 条件不为真,因此将执行 else 分支。如果有几个独立的条件需要检查,也可以使用elif语句,这比一个接一个写多个if语句要好。因此,您可以使用更紧凑的elif符号来代替编写三个单独的if语句来检查等于、小于和大于的条件:

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

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

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

自动化脚本执行

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

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

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

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

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

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

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

如你所见,它运行得很好。基于我们创建的 cron 作业,明天这里应该会有一个不同的命令行示例。

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

摘要

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

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