了解和学习基本 Shell 脚本和 Linux 文件系统故障排除 - 第 10 部分
Linux 基金会推出了 LFCS 认证(Linux Foundation Certified Sysadmin),这是一项全新的计划,其目的是让世界各地(以及任何地方)的个人获得基础到中级的认证对 Linux 系统的运营支持,包括支持正在运行的系统和服务,以及整体监控和分析,以及向上层支持团队提出问题时的明智决策。
请观看以下视频,该视频将引导您了解 Linux 基金会认证计划。
这是本系列 10 篇教程的最后一篇文章(第 10 部分)。在本文中,我们将重点介绍基本的 shell 脚本编写和 Linux 文件系统故障排除。这两个主题都是 LFCS 认证考试所必需的。
了解终端和 shell
我们先澄清几个概念。
- shell 是一个程序,它接受命令并将它们提供给操作系统来执行。
- 终端是一个允许我们作为最终用户与 shell 进行交互的程序。 GNOME 终端就是终端的一个示例,如下图所示。
当我们第一次启动 shell 时,它会显示一个命令提示符(也称为命令行),它告诉我们 shell 已准备好开始接受来自其标准输入设备(通常是键盘)的命令。
您可能需要参考本系列中的另一篇文章(使用命令创建、编辑和操作文件 - 第 1 部分)来回顾一些有用的命令。
Linux 提供了一系列 shell 选项,以下是最常见的:
bash外壳
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 可以是以下之一(此处仅引用最常见的条件),并且在以下情况下评估为 true:
- [ -a file ] → 文件存在。
- [ -d file ] → 文件存在并且是一个目录。
- [ -f file ] →文件存在并且是常规文件。
- [ -u file ] →文件存在且其 SUID(设置用户 ID)位已设置。
- [ -g file ] →文件存在且其 SGID 位已设置。
- [ -k file ] →文件存在并且其粘滞位已设置。
- [ -r file ] →文件存在且可读。
- [ -s file ]→ 文件存在且不为空。
- [ -w file ]→文件存在且可写。
- 如果文件存在并且可执行,[ -x file ] 为 true。
- [ string1=string2 ] → 字符串相等。
- [ string1 != string2 ] →字符串不相等。
[ int1 op int2 ] 应该是前面列表的一部分,而后面的项目(例如,-eq –> 如果 int1 则为 true > 等于 int2。)应该是 [ int1 op int2 ] 的“children”列表,其中 op是以下比较运算符之一。
- 如果 int1 等于 int2,-eq –> 为 true。
- -ne –> 如果 int1 不等于 int2,则为 true。
- -lt –> 如果 int1 小于 int2,则为 true。
- -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 的输出中找不到 word 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
另请阅读:
- 学习 Shell 脚本:从新手到系统管理员的指南
- 学习 Shell 编程的 5 个 Shell 脚本
文件系统故障排除
虽然 Linux 是一个非常稳定的操作系统,但如果它因某种原因崩溃(例如,由于断电),您的一个(或多个)文件系统将无法正确卸载,因此在 Linux 运行时会自动检查错误。已重新启动。
此外,每次系统在正常启动期间启动时,它总是在安装文件系统之前检查文件系统的完整性。在这两种情况下,都是使用名为 fsck(“文件系统检查”)的工具来执行的。
fsck 不仅会检查文件系统的完整性,还会根据指示尝试修复损坏的文件系统。根据损坏的严重程度,fsck 可能成功也可能失败;当它发生时,恢复的文件部分将被放置在位于每个文件系统根目录的 lost+found 目录中。
最后但并非最不重要的一点是,我们必须注意,如果我们在操作系统仍在写入 USB 驱动器时尝试移除 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考试所需的基本领域能力。
出于显而易见的原因,不可能在任何单个教程中涵盖这些主题的每个方面,这就是为什么我们希望这些文章能让您走上正确的道路,自己尝试新东西并继续学习。
如果您有任何问题或意见,我们随时欢迎 – 所以请不要犹豫,通过下面的表格给我们留言!