理解和学习基本 Shell 脚本和 Linux 文件系统故障排除 - 第 10 部分
Linux 基金会推出了LFCS认证(Linux 基金会认证系统管理员),这是一项全新的举措,目的是让世界各地的个人都能获得 Linux 系统基础到中级运营支持的认证,包括支持正在运行的系统和服务,以及整体监控和分析,以及向上级支持团队提出问题时的智能决策。
请观看以下视频,它将引导您了解 Linux 基金会认证计划。
这是本系列教程的最后一篇文章(第 10 部分),共 10 篇。本文将重点介绍基本的 shell 脚本和 Linux 文件系统故障排除。这两个主题都是 LFCS 认证考试的必修内容。
了解终端和 Shell
首先我们来明确几个概念。
- Shell 是一个接受命令并将其交给操作系统执行的程序。
- 终端是一个允许我们作为最终用户与 shell 交互的程序。终端的一个例子是 GNOME 终端,如下图所示。
当我们第一次启动 shell 时,它会显示一个命令提示符(也称为命令行),告诉我们 shell 已准备好开始从其标准输入设备(通常是键盘)接受命令。
您可能需要参考本系列的另一篇文章(使用命令创建、编辑和操作文件 - 第 1 部分)来查看一些有用的命令。
Linux 提供了一系列 shell 选项,以下是最常见的:
重击外壳
Bash代表Bourne Again SHell,是 GNU 项目的默认 shell。它整合了 Korn shell (ksh) 和 C shell (csh) 的有用功能,同时提供了多项改进。这是 LFCS 认证中涵盖的发行版使用的默认 shell,也是我们将在本教程中使用的 shell。
壳
Bourne SHell是最古老的 shell,因此多年来一直是许多类 UNIX 操作系统的默认 shell。
外壳
Korn SHell是一个 Unix shell,由 David Korn 于 20 世纪 80 年代初在贝尔实验室开发。它向后兼容 Bourne shell,并包含 C shell 的许多功能。
Shell 脚本只不过是一个文本文件,转换成一个可执行程序,它将 Shell 一个接一个执行的命令组合在一起。
基本 Shell 脚本
如前所述,shell 脚本天生就是纯文本文件。因此,可以使用我们首选的文本编辑器来创建和编辑。您可能想要考虑使用 vi/m(请参阅vi 编辑器的使用 - 本系列的第 2 部分),它具有语法高亮功能,方便您使用。
键入以下命令以创建名为 myscript.sh 的文件,然后按 Enter。
# vim myscript.sh
Shell 脚本的第一行必须如下所示(也称为 shebang )。
#!/bin/bash
它“告诉”操作系统应该使用哪个解释器来运行后面的文本。
现在该添加命令了。我们可以通过添加注释来阐明每个命令或整个脚本的用途。请注意,shell 会忽略以井号#开头的行(解释性注释)。
#!/bin/bash echo This is Part 10 of the 10-article series about the LFCS certification echo Today is $(date +%Y-%m-%d)
一旦脚本编写并保存完毕,我们就需要使其可执行。
# chmod 755 myscript.sh
在运行脚本之前,我们需要先了解一下$PATH环境变量。如果我们运行,
echo $PATH
从命令行,我们将看到$PATH的内容:当我们输入可执行程序的名称时,将搜索的目录列表(以冒号分隔)。它之所以被称为环境变量,是因为它是 shell 环境的一部分 - 一组在 shell 首次启动时可供 shell 及其子进程使用的信息。
当我们输入命令并按下 Enter 键时,shell 会在$PATH变量中列出的所有目录中搜索,并执行找到的第一个实例。让我们看一个例子,
如果有两个同名的可执行文件,一个在/usr/local/bin中,另一个在/usr/bin中,则将首先执行第一个目录中的可执行文件,而忽略另一个。
如果我们没有将脚本保存在$PATH变量中列出的目录中,则需要在文件名后附加./才能执行它。否则,我们可以像运行常规命令一样运行它。
# pwd # ./myscript.sh # cp myscript.sh ../bin # cd ../bin # pwd # myscript.sh
条件语句
每当您需要在 shell 脚本中指定要采取的不同操作过程(作为命令成功或失败的结果)时,您都将使用if构造来定义此类条件。其基本语法是:
if CONDITION; then COMMANDS; else OTHER-COMMANDS fi
其中CONDITION可以是下列之一(这里仅引用最常见的条件),并且在以下情况下计算结果为真:
- [ -a 文件 ] → 文件存在。
- [ -d 文件 ] → 文件存在并且是一个目录。
- [ -f 文件 ] →文件存在,并且是常规文件。
- [ -u 文件 ] →文件存在并且其 SUID(设置用户 ID)位已设置。
- [ -g 文件 ] →文件存在并且其 SGID 位已设置。
- [ -k 文件 ] →文件存在并且其粘滞位已设置。
- [ -r 文件 ] →文件存在并且可以读取。
- [ -s 文件 ] → 文件存在且不为空。
- [ -w 文件 ] →文件存在且可写。
- 如果文件存在且可执行,则[ -x file ]为真。
- [ string1 = string2 ] → 字符串相等。
- [ string1 != string2 ] → 字符串不相等。
[ int1 op int2 ]应为前面列表的一部分,而后面的项目(例如,如果int1等于int2则-eq –>为真)应为[ int1 op int2 ] 的“子”列表,其中 op 是以下比较运算符之一。
- -eq –>如果 int1 等于 int2,则为真。
- -ne –>如果 int1 不等于 int2,则为 true。
- -lt –>如果 int1 小于 int2,则为真。
- -le –>如果 int1 小于或等于 int2,则为 true。
- -gt –>如果 int1 大于 int2,则为 true。
- -ge –>如果 int1 大于或等于 int2,则为 true。
For 循环
此循环允许对值列表中的每个值执行一个或多个命令。其基本语法是:
for item in SEQUENCE; do COMMANDS; done
其中item是一个通用变量,代表每次迭代期间SEQUENCE中的每个值。
While 循环
此循环允许执行一系列重复命令,只要控制命令执行且退出状态等于零(成功)。其基本语法是:
while EVALUATION_COMMAND; do EXECUTE_COMMANDS; done
其中EVALUATION_COMMAND可以是任何以成功( 0)或失败(非0)状态退出的命令,而 EXECUTE_COMMANDS 可以是任何程序、脚本或 shell 构造,包括其他嵌套循环。
综合起来
我们将通过以下示例演示 if 结构和 for 循环的用法。
确定服务是否在基于 systemd 的发行版中运行
让我们创建一个文件,其中包含我们想要一目了然地监控的服务列表。
# cat myservices.txt sshd mariadb httpd crond firewalld
我们的 shell 脚本看起来应该是这样的。
#!/bin/bash # This script iterates over a list of services and # is used to determine whether they are running or not. for service in $(cat myservices.txt); do systemctl status $service | grep --quiet "running" if [ $? -eq 0 ]; then echo $service "is [ACTIVE]" else echo $service "is [INACTIVE or NOT INSTALLED]" fi done
让我们解释一下该脚本的工作原理。
1) for 循环每次读取myservices.txt文件中的 LIST 元素。该单个元素由名为 service 的通用变量表示。LIST 的输出内容如下:
# cat myservices.txt
2)上述命令括在括号中,前面有一个美元符号,表示应该对其进行评估以填充我们将要迭代的 LIST。
3).对于LIST中的每个元素(即服务变量的每个实例),将执行以下命令。
# systemctl status $service | grep --quiet "running"
这次我们需要在通用变量(代表LIST中的每个元素)前面加上一个美元符号,以表明它是一个变量,因此每次迭代都应该使用它的值。然后将输出通过管道传输到 grep。
–quiet标志用于防止grep在屏幕上显示出现单词 running 的行。发生这种情况时,上述命令将返回退出状态0 (在 if 结构中用$?表示),从而验证服务正在运行。
退出状态非0(表示在systemctl status $service的输出中未找到单词running )表明服务未运行。
我们可以更进一步,在尝试进入 for 循环之前检查myservices.txt是否存在。
#!/bin/bash # This script iterates over a list of services and # is used to determine whether they are running or not. if [ -f myservices.txt ]; then for service in $(cat myservices.txt); do systemctl status $service | grep --quiet "running" if [ $? -eq 0 ]; then echo $service "is [ACTIVE]" else echo $service "is [INACTIVE or NOT INSTALLED]" fi done else echo "myservices.txt is missing" fi
对一系列网络或互联网主机进行 ping 以获取回复统计信息
您可能想要在文本文件中维护一个主机列表,并使用脚本来不时地确定它们是否可 ping 通(请随意替换myhosts的内容并亲自尝试)。
read shell 内置命令告诉 while 循环逐行读取 myhosts 并将每行的内容赋给变量 host,然后传递给ping命令。
#!/bin/bash # This script is used to demonstrate the use of a while loop while read host; do ping -c 2 $host done < myhosts
另请阅读:
文件系统故障排除
尽管 Linux 是一个非常稳定的操作系统,但是如果它由于某种原因(例如,由于断电)崩溃,您的一个(或多个)文件系统将无法正确卸载,因此在重新启动 Linux 时会自动检查错误。
此外,每次系统在正常启动过程中启动时,它都会在挂载文件系统之前检查文件系统的完整性。在这两种情况下,这都是使用名为fsck(“文件系统检查”)的工具执行的。
fsck不仅会检查文件系统的完整性,还会在收到指示时尝试修复损坏的文件系统。根据损坏的严重程度,fsck 可能会成功,也可能会失败;如果成功,则文件的恢复部分将放置在lost+found目录中,该目录位于每个文件系统的根目录中。
最后但同样重要的一点是,我们必须注意,如果我们在操作系统仍在写入时尝试移除 USB 驱动器,也可能会出现不一致的情况,甚至可能导致硬件损坏。
fsck的基本语法如下:
# fsck [options] filesystem
检查文件系统是否有错误并尝试自动修复
为了使用 fsck 检查文件系统,我们必须首先卸载它。
# mount | grep sdg1 # umount /mnt # fsck -y /dev/sdg1
除了-y标志之外,我们还可以使用-a选项来自动修复文件系统而不询问任何问题,并且即使文件系统看起来干净也会强制检查。
# fsck -af /dev/sdg1
如果我们只对找出问题所在感兴趣(而不试图暂时修复任何问题),我们可以使用-n选项运行 fsck,它会将文件系统问题输出到标准输出。
# fsck -n /dev/sdg1
根据 fsck 输出中的错误消息,我们将知道是否可以尝试自己解决问题或将其上报给工程团队以对硬件进行进一步检查。
概括
我们已经完成了这10 篇系列文章,其中试图涵盖通过LFCS考试所需的基本领域能力。
由于显而易见的原因,不可能在任何单个教程中涵盖这些主题的每个方面,这就是为什么我们希望这些文章能让您走上正确的轨道,自己尝试新的东西并继续学习。
如果您有任何问题或意见,我们随时欢迎您 - 因此请随时通过下面的表格给我们留言!