树莓派开始,玩转Linux17:会编程的bash

树莓派开始,玩转Linux17:会编程的bash

bash是一个命令解释器。在前面章节中介绍了在bash中输入命令,它会把输入的命令转化为特定的动作。本章将介绍bash的可编程性。bash提供了某些类似于C语言的编程语法,从而允许你用编程的方式,来组合使用Linux系统。

1.变量:

正如我们在C语言中看到的,变量是内存中的一块空间,可以用于存储数据。我们可以通过变量名来引用变量中保存的数据。借助变量,程序员可以复用出现过的数据。bash中也有变量,但bash的变量只能存储文本。
1.变量赋值
bash和C类似,同样用赋值符号"="来表示赋值,比如:
在这里插入图片描述
赋值就是把文本World存入名为var的变量。根据bash的语法,赋值符号的左右不留空格。赋值符号右边的文本内容会存入赋值符号左边的变量中。

如果文本中包含空格,那么可以用单引号或双引号来包裹文本。用单引号来包裹文本,比如:
在这里插入图片描述
用双引号来包裹文本,比如:
在这里插入图片描述
在bash中,可以把一个命令输出的文本直接赋予一个变量:
在这里插入图片描述
借助``符号,date命令的输出存入了变量now。

还可以把一个变量中的数据赋值给另一个变量:
在这里插入图片描述
用户也可以直接向bash输入数据,这要用到read命令。该命令运行后,bash等待用户输入,比如:
在这里插入图片描述
命令read后面跟着的name,说明了等待存储数据的变量名。用键盘输入:

在这里插入图片描述
然后按Enter键,键盘输入的文本会赋值给变量name,打印变量name检查:
在这里插入图片描述
我们可以看到,变量name中存储的已经是刚才输入的文本"Vamei"了。

2.引用变量
我们可以用$var的方式来引用变量。在bash中,所谓的引用变量就是把变量翻译成变量中存储的文本,比如:
在这里插入图片描述
打印World,即变量中保存的文本。
在bash中,还可以在一段文本中嵌入变量。bash也会把变量替换成
变量中保存的文本,比如:
在这里插入图片描述
文本将打印出HelloWorld。

为了避免变量名和尾随的普通文本混淆,我们也可以换用${}的方
式来标识变量,比如:
在这里插入图片描述

因为bash中并没有varIsGood这个变量,所以bash将打印空白行。但如果将命令改为:
在这里插入图片描述

bash通过${}识别出变量var,并把它替换成数据,最终echo命令打印出WorldIsGood。

在bash中,为了把一段包含空格的文本当作单一参数,需要用到单引号或双引号,可以在双引号中使用变量,比如:
在这里插入图片描述
将打印出Hello World。因为bash会忽视单引号中的变量引用,所以
单引号中的变量名只会被当作普通文本,比如:
在这里插入图片描述
将打印出Hello $var。

2.数字运算:

在bash中,由于数字和运算符都被当作普通文本,因此无法像C语言一样便捷地进行数学运算,比如执行下面的命令:
在这里插入图片描述
bash并不会进行任何运算,它只会打印文本"1+2"。

在bash中,还可以通过$(())语法来进行数值运算。在双括号中可以放入整数的加、减、乘、除表达式,bash会对其中的内容进行数值运算,比如:
在这里插入图片描述

它将打印运算结果12。

此外,在$(())中,也可以使用变量,比如:
在这里插入图片描述
打印运算结果11。

可以用bash实现多种整数运算。
· 加法: ( ( 1 + 6 ) ) , 结 果 为 7 。 ⋅   减 法 : ((1+6)),结果为7。 · 减法: ((1+6))7 ((5–3)),结果为2。
· 乘法: ( ( 2 ∗ 2 ) ) , 结 果 为 4 。 ⋅   除 法 : ((2*2)),结果为4。 · 除法: ((22))4 ((9/3)),结果为3。
· 求余: ( ( 5 ⋅   乘 方 : ((5%3)),结果为2。 · 乘方: ((5 ((2**3)),结果为8。
现在,你就可以把数学运算结果存入变量了。

在这里插入图片描述
我们看到,bash支持多种多样的运算符。当一个表达式中有多个算术操作时,就必须考虑算术优先级。bash的算术优先级和数学中的算术优先级类似。优先级从高到低排序如下所示。
(1)乘方。
(2)乘法、除法和求余。
(3)加法和减法。
当优先级相等时,bash按照从左向右的顺序来进行运算。当然,像数学中那样,括号中的内容将优先执行,例如:
在这里插入图片描述
bash会先执行括号中的减法,然后依次执行乘方、乘法、除法和加法。

3.返回代码:

在Linux中,每个可执行程序会有一个整数的返回代码。按照Linux的惯例,当程序正常运行完毕并返回时,将返回整数0。因此,C程序中返回0的语句,都出现在C程序中main函数的最后一句。例如下面的foo.c程序:
在这里插入图片描述
这段程序可以正常运行。因此,它将在最后一句执行return语句,程序的返回代码是0。

在Shell中,运行程序后,可以通过$?变量来获知返回码,比如:

在这里插入图片描述
如果一个程序运行异常,那么这个程序将返回非0的返回代码,比如删除一个不存在的文件:

在这里插入图片描述
在Linux中,可以在一行命令中执行多个程序,比如:
在这里插入图片描述
在执行多个程序时,我们可以让后一个程序的运行参考前一个程序的返回代码。比如,只有前一个程序返回成功代码0时,才让后一个程序运行:

在这里插入图片描述
如果rm命令顺利运行,那么第二个echo命令将执行。

还有一种情况是等前一个程序失败了,才运行后一个程序,比如:
在这里插入图片描述
当rm命令失败时,第二个echo命令才会执行。

4.bash脚本:

多行的bash命令写入一个文件就形成了所谓的bash脚本。当bash脚本执行时,Shell将逐行执行脚本中的命令。
1.脚本的例子
用文本编辑器编写一个bash脚本hello_world.bash:
在这里插入图片描述
脚本的第一行说明了该脚本使用的Shell,即/bin/bash路径的bash程序。脚本正文是两行echo命令。运行脚本的方式和运行可执行程序的方式类似,都是:

在这里插入图片描述
需要注意的是,如果用户不具有执行bash脚本文件的权限,那么他将无法执行bash脚本。此时,用户必须更换文件权限,或者以其他身份登录,才能执行脚本。

当脚本运行时,两行命令将按照由上至下的顺序依次执行。Shell将
打印两行文本:
在这里插入图片描述
bash脚本是一种复用代码的方式。我们可以用bash脚本实现特定的功能。由于该功能记录在脚本中,因此可以通过重复运行同一个文件来实现相同的功能,而不是每次想用的时候都要重新输入一遍命令。我们看一个简单的bash脚本hw_info.bash,它将计算机的信息存入名为log的文件中:
在这里插入图片描述
在bash代码中,可以增加注释。注释以文字的形式在源代码中解释代码功能。注释不会影响代码的运行结果,但可以让代码变得更易读,也更容易维护。在bash脚本中,可以在行首用"#"符号来表示该行是注释。比如在上面的脚本中增加注释:

在这里插入图片描述
2.脚本参数
和可执行程序类似,bash脚本运行时,也可以携带参数。这些参数可以在bash脚本中以变量的形式使用,比如test_arg.bash:
在这里插入图片描述
在bash中,可以用$0、$1、$2…的方式来获得bash脚本运行时的参数,我们用下面的方式运行bash脚本:

在这里插入图片描述
$0是命令的第一部分,也就是./test_arg.bash。$1代表了参数hello,而$2代表了参数world。因此,上面的程序将打印:
在这里插入图片描述
在这里插入图片描述
变更参数,同一段脚本将有不同的行为。这大大提高了bash脚本的灵活性。在上面的hw_info.bash脚本中,我们把输出文件名写成固定的log。我们现在可以修改hw_info.bash脚本,用可变的参数作为输出文件的文件名:
在这里插入图片描述
借助参数可以自由地设置输出文件的名字:
在这里插入图片描述
3.脚本的返回代码
和可执行程序类似,脚本也可以有返回代码。按照惯例,脚本正常退出时返回代码0。在脚本的末尾,可以用exit命令来设置脚本的返回代码。我们修改hello_world.bash:
在这里插入图片描述

其实在脚本的末尾加一句exit 0并不必要。一个脚本如果正常运行
完最后一句,会自动返回代码0。在脚本运行后,可以通过$?变量查询
脚本的返回代码:
在这里插入图片描述
果在脚本中部出现exit命令,那么脚本会直接在这一行停止,并返回该exit命令给出的返回代码,比如下面的demo_exit.bash:
在这里插入图片描述
你可以运行该脚本,检查其输出结果,并查看返回代码。

5.函数:

在bash中,脚本和函数有很多相似的地方。脚本实现了一整个脚本文件的程序复用,而函数复用了脚本内部的部分程序。一个函数可以像脚本一样包含多个指令,用于说明该函数如果被调用会执行哪些活动。在定义函数时,我们需要用花括号来标识函数包括的部分。

在这里插入图片描述
脚本一开始定义了函数my_info,my_info是函数名。关键字function和花括号都提示了该部分是函数定义。因此,function关键字并不是必须的。上面的脚本等效于:
在这里插入图片描述
花括号中的三行命令就说明了函数执行时需要执行的命令。需要强调的是,函数定义只是食谱,并没有转化成具体的动作。脚本的最后一行是在调用函数。只有通过函数调用,函数内包含的命令才能真正执行。调用函数时,只需要一个函数名就可以了。像脚本一样,函数调用时还可以携带参数。在函数内部,我们同样可以用$1、$2这种形式的变量来调用参数。

在这里插入图片描述
在这里插入图片描述
在上面的脚本中,进行了两次函数调用。函数调用时分别携带了参数output.file和another_output.file。

6.跨脚本调用:

使用source命令可以实现函数的跨脚本调用。命令source的作用是在同一个进程中执行另一个文件中的bash脚本。比如,有两个脚本my_info.bash和app.bash。脚本my_info.bash中的内容是:
在这里插入图片描述
运行app.bash,执行到source命令那一行时,就会执行my_info.bash脚本。在app.bash的后续部分,就可以使用my_info.bash中的my_info函数。

本章介绍了bash的变量和运算。此外,函数和命令source提供了函数级别和脚本级别的代码复用。

===================================================================

我们已经介绍了函数和脚本两种组合命令的方式。这两种方式都可以把多行命令合并起来,组成一个功能单元。函数和脚本复用代码的方式相对机械。

本章会介绍选择和循环两种语法结构,这两种语法结构可以改变脚本的运行顺序,从而编写出更加灵活的程序。

7.逻辑判断:

bash不仅可以进行数值运算,还可以进行逻辑判断。逻辑判断是确定某个说法的真假。我们在生活中很自然地进行各种各样的逻辑判断。比如"3大于2"这个说法,我们会说它是真的。逻辑判断就是对一个说法判断真假。
在bash中,我们可以用test命令来进行逻辑判断:
在这里插入图片描述
命令test后面跟有一个判断表达式,其中的-gt表示大于,即greater than。因为"3大于2"这一表达式为真,所以命令的返回代码将是0。如果表达式为假,那么命令的返回代码是1:

在这里插入图片描述
表达式中的-lt表示小于,即less than。

数值大小和相等关系的判断是最常见的逻辑判断。

除了上面的大于和小于判断,我们还可以进行以下的数值判断。
· 等于:
在这里插入图片描述
· 不等于:
在这里插入图片描述
· 大于等于:
在这里插入图片描述
· 小于等于:
在这里插入图片描述
bash中最常见的数据形式是文本,因此也提供了很多关于文本的判断。

· 文本相同:
在这里插入图片描述
· 文本不同:
在这里插入图片描述
· 按照词典顺序,一个文本在另一个文本之前:
在这里插入图片描述
· 按照词典顺序,一个文本在另一个文本之后:
在这里插入图片描述
bash还可以对文件的状态进行逻辑判断。

· 检查一个文件是否存在:
在这里插入图片描述
· 检查一个文件是否存在,而且是普通文件:
在这里插入图片描述
· 检查一个文件是否存在,而且是目录文件:
在这里插入图片描述
· 检查一个文件是否存在,而且是软链接:
在这里插入图片描述
在这里插入图片描述
· 检查一个文件是否可读:
在这里插入图片描述
· 检查一个文件是否可写:
在这里插入图片描述
· 检查一个文件是否可执行:
在这里插入图片描述
在做逻辑判断时,可以把多个逻辑判断条件用"与、或、非"的关系组合起来,形成复合的逻辑判断。
在这里插入图片描述
8.选择结构:

逻辑判断可以获得计算机和进程的状态。bash可以根据逻辑判断,让程序有条件地运行,这就是所谓的选择结构。选择结构是一种语法结构,可以让程序根据条件决定执行哪一部分指令。最早的程序都是按照指令顺序依次执行的。选择结构打破了这一顺序,给程序带来更高的灵活性。最简单的,我们可以根据条件来决定是否执行某一部分程序,比如下面的demo_if.bash脚本:
在这里插入图片描述
这个脚本使用了最简单的if结构。关键字if后面跟着[],里面是一个逻辑表达式。这个逻辑表达式就是if结构的条件。如果条件成立,那么if将执行then到fi之间包含的语句,我们称之为隶属于if的代码块。如果条件不成立,那么隶属于if的代码块不执行。因此,if结构的流程如图所示。
在这里插入图片描述
这个例子的条件是判断用户是否为root。因此,如果是非root用户执行该脚本,那么Shell不会打印任何内容。

我们还可以通过if else结构,让bash脚本从两个代码块中选择一个执行。该选择结构同样有一个条件。如果条件成立,那么将执行if附属的代码块,否则执行else附属的代码块,流程如图所示。
在这里插入图片描述
下面的demo_if_else.bash脚本是if else结构的一个小例子:

在这里插入图片描述
if后面的"-e $filename"作为判断条件。如果条件成立,即文件存在,那么执行then后面的代码;如果文件不存在,那么脚本将执行else语句中的echo命令,末尾的fi结束整个语法结构。脚本继续以顺序的方式执行剩余内容。运行脚本:
在这里插入图片描述
脚本会根据a.out是否存在打印出不同的内容。

我们看到,在使用if…else结构时,我们可以实现两部分代码块的选择执行。而在if代码块和else代码块内部,可以继续嵌套选择结构,从而实现更多个代码块的选择执行。比如脚本demo_nest.bash:

在这里插入图片描述
在这里插入图片描述
在bash下,还可以用case语法来实现多程序块的选择执行,比如下面的脚本demo_case.bash:
在这里插入图片描述
这个脚本和上面的demo_nest.bash功能完全相同,可以看到case结构与if结构的区别。关键字case后面不再是逻辑表达式,而是一个作为条件的文本。后面的代码块分为三个部分,都以文本标签的形式开始,以;;结束。case结构运行时会逐个检查文本标签。当条件文本和文本标签对应时,bash就会执行隶属于该文本标签的代码块。如果是用户vamei执行该bash脚本,那么条件文本和vamei标签对应上,脚本就会打印:
在这里插入图片描述
文本标签除了是一串具体的文本,还可以包含文本通配符。结构case中常用的通配符,如表所示。
在这里插入图片描述
在上面的程序中,最后一个文本标签是通配符*,即表示任意条件文本都可以触发此段代码块运行。当然,前提是前面的几个文本标签都没有触发代码运行。

9.循环结构:

循环结构是编程语言中一种常见的语法结构。循环结构的功能是重复执行某一段代码,直到计算机的状态符合某一条件。在while语法中,bash会循环执行隶属于while的代码块,直到逻辑表达式不成立。比如下面的demo_while.bash:
在这里插入图片描述
关键字do和done之间的代码是隶属于该while结构的代码块。在while后面跟着条件,该条件决定了代码块是否重复执行下去。这个条件是用当前的时间与目标时间对比。如果当前时间小于目标时间,那么代码块就会重复执行下去。否则,bash将跳出循环,继续执行后面的语句,流程如图所示。
在这里插入图片描述
如果while的条件始终为真,那么循环会一直进行下去。下面的程序以无限循环的形式,不断播报时间。
在这里插入图片描述
语法while的终止条件是一个逻辑判断。如果在循环过程中改变逻辑判断的内容,那么我们很难在程序执行之前预判循环进行的次数。正如之前在demo_while.bash中看到的,我们在循环进行过程中改变着作为条件的逻辑表达式,不断地更新参与逻辑判断的当前时间。与while语法对应的是for循环。这种语法会在程序进行前确定好循环进行的次数,比如demo_for.bash:
在这里插入图片描述
在这个例子中,命令ls log*将返回所有以log为开头的文件名,这些文件名之间由空格分隔。当循环进行时,bash会依次取出一个文件名,赋值给变量var,并执行do和done之间隶属于for结构的程序块。由于ls命令返回的内容是确定的,因此for循环进行的次数也会在一开始确定下来。
在for语法中,也可以使用自己构建一个由空格分隔的文本。由空格区分出来的每个子文本会在循环中赋值给变量,比如:
在这里插入图片描述
此外,for循环还可以和seq命令配合使用。命令seq用于生成一个等差的整数序列,命令后面可以跟3个参数,第一个参数表示整数序列的开始数字,第二个参数表示每次增加多少,第三个参数表示序列的终点。因此,下面命令:
在这里插入图片描述
将返回:
在这里插入图片描述
可以看到,seq返回的也是由空格分隔开的文本。因此,seq的返回结果也可用于for循环。

结合for循环和seq命令,我们可以解一些有趣的数学问题。比如高斯求和,即计算从1到100的所有整数的和,可以用bash解决。

在这里插入图片描述
这个问题还可以用do while循环来求解。

在这里插入图片描述
这里break语句的作用是在满足条件时跳出循环。

如果想计算1到100所有不被3整除的数的和,则可以使用continue语句,跳过所有被3整除的数。
在这里插入图片描述
在这里插入图片描述
10.bash和C语言:

到了这里,我们已经介绍完bash语言的基本语法。bash语言和C语言都是编程语言,它们都能通过特定的语法来编写程序,而程序运行后都能实现某些功能。虽然在语法细节上存在差异,但两种语言都有以下语法。

· 变量:在内存中储存数据。
· 循环结构:重复执行代码块。
· 选择结构:根据条件执行代码块。
· 函数:复用代码块。

编程语言的作者在设计语言时,往往会借鉴已有编程语言的优点,这是不同编程语言具有相似性的一大原因。程序员往往要掌握不止一套编程语言。相似的语法特征,会让程序员在学习新语言时感到亲切,从而促进语言的推广。

bash和C的相似性,也来自于它们共同遵守的编程范式------面向过程编程。支持面向过程编程的语言,一般都会提供类似于函数的代码封装方式。函数把多行指令包装成一个功能。只要知道了函数名,程序可以通过调用函数来使用函数功能,最终实现代码复用。

除了面向过程编程,还有面向对象和函数式的编程范式。每种编程范式都提供了特定的代码封装方式,并达到代码复用的目的。值得注意的是,近年来出现的新语言往往会支持不止一种编程范式。除了相似性,还应该注意到bash和C程序的区别
bash的变量只能是文本类型,C的变量却可以有整数、浮点数、字符等类型。bash的很多功能,如加、减、乘、除运算,都是通过调用其他程序实现的,而C程序直接就可以进行加、减、乘、除运算。可以说,C语言是一门真正的编程语言,C程序最终会编译成二进制的可执行文件,CPU可以直接理解这些文件中的指令。
一方面,bash 是一个Shell,它本质上是一个命令解释器程序,而不是编程语言。用户可以通过命令行的方式来调用该程序的某些功能。所谓的bash编程,只是命令解释器程序提供的一种互动方法。bash脚本只能和bash进程互动。

它不能像C语言一样,直接调用CPU的功能。因此,bash能实现的功能会受限,运行速度也比不上可执行文件。另一方面,bash脚本也有它的好处。C语言能接触到底层的东西,但使用起来很复杂。有时候,即使已经知道如何用它实现一个功能,写代码依然是一个很烦琐的过程,而bash正相反。

由于bash可以便捷地调用已有的程序,因此很多工作可以用数行脚本解决。此外,bash脚本不用编译就可以由bash进程理解并执行,因此开发bash脚本比写C程序要快很多。Linux的系统运维工作,如定期备份、文件系统管理等,就经常使用到bash脚本。总之,bash编程知识是晋级为资深Linux用户的必要条件

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值