white and brown concrete building

Bash 编程入门第一部分:语法和工具

了解如何使用 Bash(大多数 Linux 的默认 shell)在您的计算机上自动化任务…
首页 » 博客 » Bash 编程入门第一部分:语法和工具

在本多部分系列文章的第一篇中,学习基本的 Bash 编程语法和工具,以及如何使用变量和控制运算符。

作者注:本系列文章最初发表于 Opensource.com。最初的文章由两个系列组成,其余的作为独立文章发表。这些文章都已合并为一个系列,并进行了更新和一些重要的修改,以达到此目的。

简介

但是,如果您仅将 Bash 视为 shell,您将错过其真正的强大功能。在研究我的三卷本Linux 自学课程(本系列文章的基础)时,我了解了关于 Bash 的一些事情,这些事情在我使用 Linux 超过 20 年的时间里从未了解过。其中一些新知识与它作为编程语言的用途有关。Bash 是一种强大的编程语言,非常适合在命令行和 shell 脚本中使用。

本系列探讨将 Bash 用作命令行界面 (CLI) 编程语言。第一篇文章着眼于使用 Bash 进行的一些简单的命令行编程、变量和控制运算符。

即将发表的文章将探讨 Bash 文件的类型;提供执行流控制逻辑的字符串、数字和杂项逻辑运算符;不同类型的 shell 扩展;以及启用重复操作的 forwhileuntil 循环。它们还将研究一些简化和支持使用这些工具的命令。

本系列后面的文章将探讨使用 Bash 自动化懒惰系统管理员的工作,创建一个

Shell

Shell 是操作系统的命令解释器。Bash 是我最喜欢的 shell,但每个 Linux shell 都会将用户或系统管理员键入的命令解释为操作系统可以使用的形式。当结果返回到 shell 程序时,它会将数据流发送到 STDOUT,默认情况下,STDOUT 会在终端中显示它们。我熟悉的所有 shell 也都是编程语言。

诸如制表符补全、命令行调用和编辑以及别名之类的快捷方式等功能都有助于其作为强大 shell 的价值。它的默认命令行编辑模式使用 Emacs,但我最喜欢的 Bash 功能之一是,我可以将其更改为 Vi 模式,以使用已经是我的肌肉记忆一部分的编辑命令。

Bash 代表 Bourne Again Shell,因为 Bash shell 是基于 Steven Bourne 于 1977 年编写的较旧的 Bourne shell。有许多其他 shell 可用,但以下是我最常遇到的四种 shell

  • csh: C shell,适用于喜欢 C 语言语法的程序员
  • ksh: Korn shell,由 David Korn 编写,在 Unix 用户中很受欢迎
  • tcsh: csh 的一个版本,具有更多易用性功能
  • zsh: Z shell,它结合了其他流行 shell 的许多功能

所有 shell 都有内置命令,用于补充或替换核心实用程序提供的命令。打开 shell 的 man 页面并找到“BUILT-INS”部分,以查看它提供的命令。

每个 shell 都有其自身的个性和语法。有些可能比其他的更适合您。我使用过 C shell、Korn shell 和 Z shell。我仍然比它们中的任何一个都更喜欢 Bash shell。使用最适合您的那个,尽管这可能需要您尝试其他一些 shell。幸运的是,更改 shell 非常容易。

所有这些 shell 都是编程语言,也是命令解释器。本系列的主题是将 Bash 作为系统管理员的编程语言的应用。让我们首先看看语法以及 Bash 的组成部分工具。

作为编程语言的 Bash

大多数系统管理员都使用 Bash 发出命令,这些命令通常非常简单明了。但是 Bash 可以超越输入单个命令,许多系统管理员创建简单的命令行程序来执行一系列任务。这些程序是常见的工具,可以节省时间和精力。

我编写 CLI 程序的目的是节省时间和精力(即,成为懒惰的系统管理员)。CLI 程序通过列出按特定顺序执行的多个命令来支持这一点,因此您无需观看一个命令的进度,并在第一个命令完成后键入下一个命令。您可以去做其他事情,而不必不断监视每个命令的进度。

什么是“程序”?

《免费在线计算词典》(FOLDOC) (FOLDOC) 将程序定义为:“计算机执行的指令,而不是运行它们的物理设备。”普林斯顿大学的 WordNet 将程序定义为:“……计算机可以解释和执行的指令序列……”

因此,程序可以由一个或多个执行特定相关任务的指令组成。计算机程序指令也称为程序语句。对于系统管理员来说,程序通常是一系列 shell 命令。所有可用于 Linux 的 shell,至少我熟悉的 shell,都至少具有基本形式的编程能力,而 Bash(大多数 Linux 发行版的默认 shell)也不例外。

虽然本系列使用 Bash(因为它非常普遍),但如果您使用不同的 shell,则通用编程概念将是相同的,尽管构造和语法可能略有不同。某些 shell 可能支持某些其他 shell 不支持的功能,但它们都提供某种编程能力。Shell 程序可以存储在文件中以供重复使用,也可以根据需要在命令行上创建。

简单的 CLI 程序

最简单的命令行程序是一个或两个连续的程序语句,它们可能是相关的或不相关的,在按下 Enter 键之前在命令行上输入。程序中的第二个语句(如果存在)可能依赖于第一个语句的操作,但它不必如此。

还需要明确说明一个语法标点符号。在命令行上输入单个命令时,按 Enter 键会使用隐式分号 (;) 终止命令。当在作为单行在命令行上输入的 CLI shell 程序中使用时,必须使用分号来终止每个语句并将其与下一个语句分隔开。CLI shell 程序中的最后一个语句可以使用显式或隐式分号。

一些基本语法

以下示例将阐明此语法。此程序由一个带有显式终止符的命令组成

$ echo "Hello world." ;
Hello world.

这看起来可能不像一个程序,但它是我学习每种新编程语言时遇到的第一个程序。每种语言的语法可能略有不同,但结果是相同的。

让我们稍微扩展一下这个微不足道但无处不在的程序。您的结果将与我的结果不同,因为我做过其他实验,而您可能只有首次通过 GUI 桌面登录帐户时在帐户主目录中创建的默认目录和文件。

$ echo "My home directory." ; ls ;
My home directory.
chapter25   TestFile1.Linux  dmesg2.txt  Downloads  newfile.txt  softlink1  
chapter26   TestFile1.mac    dmesg3.txt  file005    Pictures     Templates  
TestFile1   Desktop          dmesg.txt   link3      Public       testdir    
TestFile1.dos  dmesg1.txt    Documents   Music      random.txt   testdir1

这更有意义。结果是相关的,但各个程序语句是彼此独立的。请注意,我喜欢在分号前后放置空格,因为它使代码更易于阅读。再次尝试那个小的 CLI 程序,结尾处没有显式分号

$ echo "My home directory." ; ls 

输出没有区别。

关于变量的说明

与所有编程语言一样,Bash shell 可以处理变量。变量是符号名称,它引用内存中包含某种值的特定位置。变量的值是可变的,即它是可变的。

Bash 不像 C 和相关语言那样对变量进行类型化,将它们定义为整数、浮点数或字符串类型。在 Bash 中,所有变量都是字符串。作为整数的字符串可以用于整数算术,这是 Bash 能够进行的唯一类型的数学运算。如果需要更复杂的数学运算,可以在 CLI 程序和脚本中使用 bc 命令

变量被赋值,并且可以用于在 CLI 程序和脚本中引用这些值。变量的值是使用其名称设置的,但前面没有 $ 符号。赋值 VAR=10 将变量 VAR 的值设置为 10。要打印变量的值,可以使用语句 echo $VAR。从文本(即非数字)变量开始。

Bash 变量成为 shell 环境的一部分,直到它们被取消设置。

检查尚未赋值的变量的初始值;它应该是 null。然后为变量赋值并打印它以验证其值。您可以在单个 CLI 程序中完成所有这些操作

$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;
Hello World

注意:变量赋值的语法非常严格。在赋值语句中,等号 (=) 的两侧都不能有空格。

空行表示 MyVar 的初始值为 null。更改和设置变量的值以相同的方式完成。此示例显示了原始值和新值。

如前所述,Bash 可以执行整数算术计算,这对于计算对数组中元素位置的引用或进行简单的数学问题很有用。它不适合科学计算或任何需要小数的计算,例如财务计算。对于这些类型的计算,有更好的工具。

这是一个简单的计算

$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63

当您执行产生浮点数的数学运算时会发生什么?

$ Var1="7" ; Var2="9" ; echo "Result = $((Var1/Var2))"
Result = 0
$ Var1="7" ; Var2="9" ; echo "Result = $((Var2/Var1))"
Result = 1

结果是最接近的整数。请注意,计算是作为 echo 语句的一部分执行的。由于 Bash 优先级顺序,数学运算在封闭的 echo 命令之前执行。有关详细信息,请参阅 Bash man 页面并搜索“precedence”。

控制运算符

Shell 控制运算符是用于轻松创建一些有趣的命令行程序的语法运算符之一。CLI 程序的最简单形式只是在命令行上按顺序将多个命令串在一起

command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;

只要不发生错误,这些命令都可以毫无问题地运行。但是,当发生错误时会发生什么?您可以使用内置的 &&|| Bash 控制运算符来预测和允许错误。这两个控制运算符提供了一些流控制,使您能够更改代码执行的顺序。分号也被认为是 Bash 控制运算符,换行符也是如此。

&& 运算符只是说,“如果 command1 成功,则运行 command2。如果 command1 因任何原因失败,则跳过 command2。”该语法如下所示

command1 && command2

现在,查看一些将创建一个新目录的命令,如果成功,则使其成为当前工作目录 (PWD)。确保您的主目录 (~) 是 PWD。首先在 /root 中尝试此操作,这是一个您无权访问的目录

$ Dir=/root/testdir ; mkdir $Dir/ && cd $Dir
mkdir: cannot create directory '/root/testdir/': Permission denied

错误由 mkdir 命令发出。您没有收到指示无法创建文件的错误,因为目录创建失败。&& 控制运算符检测到非零返回代码,因此跳过了 touch 命令。使用 && 控制运算符可防止 touch 命令运行,因为创建目录时出错。这种类型的命令行程序流控制可以防止错误复合并使事情变得一团糟。但是现在是时候变得更复杂一点了。

当初始程序语句返回大于零的代码时,|| 控制运算符允许您添加另一个要执行的程序语句。基本语法如下所示

command1 || command2 

此语法的含义是,“如果 command1 失败,则执行 command2。”这意味着如果 command1 成功,则跳过 command2。尝试通过尝试创建一个新目录来执行此操作

$ Dir=/root/testdir ; mkdir $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.

这正是您所期望的。由于无法创建新目录,因此第一个命令失败,从而导致执行了第二个命令。

结合这两个运算符可提供两全其美的效果。当使用 &&|| 控制运算符时,使用一些流控制的控制运算符语法采用这种通用形式

preceding commands ; command1 && command2 || command3 ; following commands

此语法可以这样表述:“如果 command1 以返回代码 0 退出,则执行 command2,否则执行 command3。”试试看

$ Dir=/root/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.

现在,再次尝试最后一个命令,使用您的主目录而不是 /root 目录。您将有权创建此目录

$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
[student@studentvm1 testdir]$

控制运算符语法(如 command1 && command2)之所以有效,是因为每个命令都会向 shell 发送一个返回代码 (RC),指示它是否成功完成或执行期间是否存在某种类型的故障。按照惯例,返回代码零 (0) 表示成功,任何正数都表示某种类型的故障。系统管理员使用的一些工具只返回一个 (1) 来指示故障,但许多工具使用其他代码来指示发生的故障类型。

Bash shell 变量 $? 包含来自上一个命令的返回代码。脚本、命令列表中的下一个命令,甚至系统管理员都可以非常轻松地检查此返回代码。首先运行一个简单的命令并立即检查返回代码。返回代码将始终是您查看它之前运行的最后一个命令的返回代码。

$ ll ; echo "RC = $?"
total 1264
drwxrwxr-x  2 student student   4096 Mar  2 08:21 chapter25
drwxrwxr-x  2 student student   4096 Mar 21 15:27 chapter26
-rwxr-xr-x  1 student student     92 Mar 20 15:53 TestFile1
<snip>
drwxrwxr-x. 2 student student 663552 Feb 21 14:12 testdir
drwxr-xr-x. 2 student student   4096 Dec 22 13:15 Videos
RC = 0

在这种情况下,返回代码为零,这意味着命令已成功完成。现在在 root 的主目录(您无权访问的目录)上尝试相同的命令

$ ll /root ; echo "RC = $?"
ls: cannot open directory '/root': Permission denied
RC = 2
[student@studentvm1 testdir]$

在这种情况下,返回代码为 2;这意味着非 root 用户访问无权访问的目录的权限被拒绝。控制运算符使用这些返回代码使您能够更改程序执行的顺序。

总结

本文将 Bash 视为一种编程语言,并探讨了其基本语法和基本工具。它展示了如何将数据打印到 STDOUT 以及如何使用变量和控制运算符。本系列中的下一篇文章将介绍许多控制指令执行流程的 Bash 逻辑运算符。

作者

如果您喜欢这篇文章,您可能也会喜欢这些