嵌入式 Linux 入门(六、Shell 脚本编程下:Shell 脚本语法)
前言
上文我们初次认识了 Shell 脚本,本文我们就要学习 Shell 脚本的语法 ,争取做到学完本文,你也会写 Shell 脚本。
嵌入式 Linux 入门系列博文:
嵌入式 Linux 入门(一、Linux 基本介绍及文件结构)
https://xie.infoq.cn/article/1adbc03be480d8203f8b1ed4a
嵌入式 Linux 入门(二、Linux 文件系统、文件类型及权限管理)
https://xie.infoq.cn/article/4d2e926fda47f2eff019ae0e4
嵌入式 Linux 入门(三、Linux Shell 及常用命令说明)
https://xie.infoq.cn/article/b25c452df449abc1806ba446a
嵌入式 Linux 入门(四、Linux 下的编辑器 — 让人爱恨交加的 vi )
https://xie.infoq.cn/article/d87223a72889208660ecbec17
嵌入式 Linux 入门(五、Shell 脚本编程上:认识 Shell 脚本)
https://xie.infoq.cn/article/16e4569fa32dce00a39eaddaf
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
一、先看几个脚本
学习一门语言,我们要知道我们将来完成的程序是什么样子的,所以有必要先来看几个脚本。
1.1 启动某个应用程序
一个简单的脚本,基本上都是我们熟悉的 Shell 命令组成的:
1.2 USB WIFI 脚本
此脚本为 正点原子阿尔法 Linux 开发板 资料中的 USB WIFI 脚本:
二、Shell 脚本语法
上面脚本看完,有编程语言基础的应该都勉勉强强能看懂一二,但是也有很多符号啊,关键字啊与我们曾经嵌入式开发的 C 语言都不一样,带着这些疑问,开始我们的 Shell 脚本语法学习。
2.1 Shell 脚本中的符号
首先,我们先熟悉一下 Shell 脚本中的一些常用符号,然后再去说明 Shell 的基本语法。
Shell 脚本中的符号有很多,我们这里只讲解一些常用的符号,如果后期用到再来更新。
2.1.1 #
#
符号: 注释
开头的 #!
是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
出开头外的 #
号作为注释的开头字母,每一行语句中,从 #号开始的部分就不执行了,类似于 C 语言的//
2.1.2 $
$
符号(美元符号):变量替换(Variable Substitution)的代表符号,用来表示变量的值。
比如一个变量 TESTA 的值为 456 ,使用 $TESTA
就可以得到 456 这个值。
2.1.3 ' '
''
单引号:被单引号用括住的内容,将被视为单一字串,shell 不会将一对单引号之间的任何字符做特殊解释。
在引号内的代表变量的$
符号,也没有作用,他被视为一般符号处理,防止任何变量替换。
2.1.4 " "
" "
双引号:被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。
不能识别命令,可以识别变量。如 #不再是注释的开头,它只表示一个井号“#”。但 $ 仍然保持特殊含义。
2.1.5 ``
`` 倒引号:命令替换,在倒引号内部的 shell 命令首先被执行,其结果输出代替用倒引号括起来的文本。
在前面的单双引号,括住的是字串,但如果该字串是一列想要执行的命令,该怎么处理?要处理这种情况,我们得用倒单引号来做。
倒引号估计很多人都不知道在哪里,就是键盘 ESC 下面,字母这边的数字键 1 的左边,使用英文输入法,就能打出倒引号。
在 执行命令 或 运算 的时候,都需要使用倒引号。
2.1.6 \
\
反斜杠 :
转义字符,和 C 语言类似;
续行符,放在指令的最末端,表示指令连接下一行,也和 C 类似;
在文本中,跟在\后面的一个字符不会被 shell 特殊解释。
2.1.7 {}
{}
大括号 :这里只说一个和 $符号配合,作为字符串连接来使用。
2.1.8 ()
()
小括号:
命令组,用括号将一串连续指令括起来,这种用法对 shell 来说,称为指令群组, 括号中的命令将会新开一个子 shell 顺序执行,所以括号中的变量不能够被脚本余下的部分使用。
命令替换,等价于
cmd
。初始化数组。
估计要实际测试才能够理解 = =!
2.1.9 [ ]
[ ]
中括号:这里也只说一个,常出现在流程控制中,扮演括住判断式的作用。
比如:
2.1.10 ;
;
分号:主要用于区分在同一行的不同语句。
在 Shell 编程中分段的语句,如果写成单行,需要用分号进行区分,如果写成块,那么则用换行符替代了分号。
比如下面示例:
和
2.2 变量
运行 shell 时,会同时存在三种变量:
局部变量:局部变量在脚本或命令中定义,仅在当前 shell 实例中有效。本小结将要说明的变量定义与引用主要讲的就是这个局部变量。
环境变量:所有的程序,包括 shell 启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候 shell 脚本也可以定义环境变量。
shell 变量:shell 变量是由 shell 程序设置的特殊变量。shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了 shell 的正常运行
关于环境变量,我们会单独开一篇文章说明,大概就在本文结束的下一篇文章。
2.2.1 定义变量
使用=
进行变量定义, 在 shell 中赋值的=两边是不能有空格的!
变量名的命名须遵循如下规则:
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
中间不能有空格,可以使用下划线 _。
不能使用标点符号。
不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。
shell 脚本语言是弱类型语言,定义变量时不需要指定类型。
3 种方式定义变量:
如果不加双引号或者单引号,如果字符串中有空格或者 TAB 按键,则没法识别出来,如下示例(示例中用到了变量使用方式,就在下文可以看到,这里为了说明这个问题,提前使用):
双引号,单引号有什么区别呢? 这就要结合上面我们所说的知识类分析了:
建议变量定义都加双引号。
2.2.2 使用变量
基本使用
使用一个定义过的变量,只要在变量名前面加美元符号$
即可:
变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界 。
把所有变量加上花括号,这是个好的编程习惯!
在上面我们已经给过使用变量的示例了,这里就不放示例。
重命名变量
重命名变量和 C 语言一样,在后面直接重新赋值即可,如下图:
将命令的值赋值给变量
变量可以直接赋值,也可以赋值为 命令运行的结果,使用如下方式:
只读变量
删除变量
删除之后不可访问,删除不掉只读变量,示例如下图:
2.2.3 特殊变量
在 Shell 脚本中,有一些特殊的变量,前面我们说过$
符号用来表示变量的值,这些特殊变量都是以$
开头的,如下:
$*
与$@
区别
相同点:都是引用所有参数
不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,则 " * " 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。
$*
与 $@
区别在下文 2.7 流控语句中的 2.7.3 for in 语句有示例说明
2.3 数据类型
介绍一下 shell 脚本中的数据类型: 字符串 与 数组;
2.3.1 字符串
字符串是 shell 编程中最常用最有用的数据类型,字符串可以用单引号,也可以用双引号,也可以不用引号。
拼接字符串
拼接字符串直接连在一起就可以,具体可以自己体验一下
name=“this is”" my name"; name=“this is my name”; name=“this” is “my name”
等效
name=‘this is'' my nam'; name=‘this is my name'; name=‘this' is ‘my name'
等效
看示例:
获取字符串长度
在${}
中使用#
获取长度:
提取子字符串
变量后面跟一个:第几个开始:取几个数字
,字符串从 0 开始计数:
2.3.2 数组
理论的知识引用至 菜鸟教程,我们都会通过实例展示一下。
bash 支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
读取数组
这里和 C 语言数组类似:
使用 @ 符号可以获取数组中的所有元素
获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,在${}
中使用#
获取长度:
2.4 注释
在前面我们说 Shell 脚本符号的时候,我们知道了使用 #
号可以表示注释,类似于 C 语言多种的 //
.
多行注释,在 C 中我们比较简单的使用 /* ...... */
,如下:
那么 Shell 脚本中的多行注释怎么处理呢?
EOF 也可以使用其他符号:
2.5 读取控制台输入
使用 read 读取控制台输入:
选项: -p:指定读取值时的提示符;-t:指定读取值时等待的时间(秒)。参数:变量:指定读取值的变量名
使用直接看示例:
2.6 运算符
和其他编程语言一样,Shell 脚本也支持多种运算符。
2.6.1 基本算数运算
下图引用菜鸟教程:
上面的 expr 是一款表达式计算工具,教程解释道原生 bash 不支持简单的数学运算,所以需要用 expr。
其中数字运算的时候,使用乘法符号 " * "
,要用转义字符 " \ "
进行转义。
但是我们除了使用 expr 还可以使用 (( ))
,具体如下示例:
2.6.2 条件判断
在 Shell 脚本中,检测某个条件是否成立的方式如下:
关于逻辑判断的符号:
[ ] : 中括号旁边和运算符两边必须添加空格 (可以使用,不推荐)
[[ ]]:中括号旁边和运算符两边必须添加空格 (字符串验证时,推荐使用)
(()) : 中括号旁边和运算符两边必须添加空格 (数字验证时,推荐使用)
[[]] 和 (()) 分别是[ ]的针对数学比较表达式和字符串表达式的加强版。使用[[ … ]]条件判断结构,而不是[ … ],能够防止脚本中的许多逻辑错误。
对整数进行数学运算用 (( ))
,不需要使用上面的 -eq
那些运算符,直接 ((a < b))
即可,还可以做加减法,比如 a=$((a+1))
就表示 a 加 1 。
2.6.3 布尔运算
布尔运算也和 C 语言意思类似,好理解,主要在于如何使用:
(在写这个示例的时候把自己搞蒙圈了,忽略打印的字符串说明,具体见下面逻辑运算说明 = =!)
在上面的示例中,我加了条件判断的 if 语句,这个在下文我们讲解条件语句的时候会说明。
2.6.4 逻辑运算
逻辑运算有与 &&
和 或||
,和 C 语言一样这个好理解。
但是我在写文章的时候,写示例,把自己高蒙圈了,如下图:
搞得我自己一下子都蒙了,尴尬,搞得我写了一个 c 理一理思路:
这个中括号上面只需要一个,这里需要 2 个,这里注意一下!
2.7 流控语句
流控语句,就是 那些 if,while,for 等循环选择语句。
2.7.1 if 语句
if 语句相对比较简单的,记住他的基本格式就好:
if else
if else-if else
直接做个测试:
2.7.2 case in 语句
类似于 C 语言中的 swtich 语句。
每个 case 分支用右圆括号)
开始,用两个分号 ;;
表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。
基本格式如下:
取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;
。
如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令,类似于 C 语言中的 default,示例如下:
2.7.3 for in 语句
for in 语句这里就和 C 区别大了,记住格式把:
当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。
in 列表可以包含替换、字符串和文件名。
in 列表是可选的,如果不用它,for 循环使用命令行的位置参数。
再看 $*
与 $@
区别 ,通过下面的示例说明:
没有双引号时候效果一样:
有双引号时候有了区别:
2.7.4 while 循环
while 循环相对也比较简单,其语法格式为:
2.7.5 跳出循环
类似 C 语言中的用法,Shell 使用两个命令来实现该功能:break 和 continue 。
break 跳出所有循环(终止执行后面的所有循环)。
continue 跳出当前循环。
2.7.6 sleep
sleep 就相当于 C 语言的延时函数,有时候在执行指令的时候需要等待上一条指令执行,我们需要加入延时, sheep 用法如下:
2.8 函数
和 C 语言一样,shell 可以用户定义函数,在 shell 脚本中调用。
函数的格式如下:
或者不带 function 也可以:
return 返回,如果不加,将以最后一条命令运行结果作为返回值 。
函数示例如下:
2.9 输入/输出重定向
输入/ 输出重定向是什么?
2.9.1 重定向的定义和用途
我用简单的白话说明一下,就是你在 Linux 下打开一个终端,输入 Shell 命令(这个你输入的命令就输入,这很好理解),输入 Shell 命令以后,会根据你输入的命令显示结果(这个结果,就是你的输出,是直接在你打开的这个终端下面显示,就是输出就在这个终端显示)。
我们说重定向,比如输出重定向,就是本来你输入的 Shell 命令结果会在你这个终端中显示的,但是你给他 重新定了一个方向,让他输出到其他地方,比如某个文件,就叫做输出重定向。
举个简单的例子,比如你敲 ls 指令查看当前文件夹下面的目录,使用 ls 输出重定向以后,它就不会在你这个终端中直接显示出 ls 的结果,而是到你重定向的那个地方去了,比如:
在一般情况下,我们很少用到输入重定向,所以本文分析输入重定向,知道一下即可,输出重定向我们倒是用得比较多。
那么这个重定向有什么意义?
☆ 输出重定向可以把命令的结果保存到文件中,目的在于可以给管理员做记录分析,或者对其进行更进一步的处理。☆
2.9.2 Linux 下的标准输入输出
这一小节即便暂时不明白也没什么关系,了解记一下就好。
我们在计算机中,标准的输入输出设备为 键盘 和显示器,我们知道在 Linux 下,一切都是文件,在 Linux 下 把输出设备分为了 2 个文件,一个正确输出,一个错误输出。
一般情况下,每个 Linux 命令运行时都会打开三个文件:
2.9.3 重定向命令
重定向使用如下命令操作:
默认情况下,command > file 将 stdout 重定向到 file,command < file 将 stdin 重定向到 file。
上面列表中那么多,要想理解需要自己试一试,结合实例才更加直观的可以理解,这里我举几个简单的例子。
标准输出重定向示例:
标准错误输出重定向示例:
那么问题来了,正确命令和错误命令的输出格式不一样,我们需要人为判断一下正确命令还是错误命令?
我要是能知道这条命令是错的(有时候粗心也会写错),我直接改正不就好了,所以我们要讲如何把正确的输出和错误的输出同时保存在指定的文件之中的方式。
正确和错误输出同时保存
先来看下正确错误同时保存的指令,下面的指令都是同时保存至同一个文件:
正确和错误输出分开保存
直接看示例:
分开保存的方式也是常见的应用,这样我们在查看脚本运行出错的时候查找问题可以直接查看错误输出以便快速定位问题。
2.9.4 /dev/null 文件
/dev/null 文件,相当于垃圾箱,是在我们希望某个命令执行但是又不需要他的输出的时候使用的。
根据我们上面重定向的学习,他的基本指令为:
再根据我们上面的学习,我们想要把正确和错误信息都不需要输出,可以使用下面的指令:
那么这里又有一个问题,这样做的意义在哪里?
简单举个例子说明,我们在写 Shell 脚本的时候,有些命令行会有一些输出,这个输出对我们整个 Shell 脚本没有任何意义,Shell 脚本批量执行的时候会有很多输出,这种没有意义的输出我们就需要把它们给屏蔽了,就可以使用上面的方式给它“丢到垃圾箱”。
结语
作为一门语言的学习,本文的内容相对还是很多的,对于本文的学习,一定要自己动手练习。
如果你未来的工作是做运维,那么必须得熟练的学会 Shell 脚本编程,如果是嵌入式 Linux 学习,那么相对来说一般用不到特别复杂的脚本,不要一开始就被劝退,那么大家一起加油吧 ヾ(◍°∇°◍)ノ゙ !
另外提一下,Shell 脚本对命令中多余的空格不进行任何的处理,所以我们在平时编写程序的时候为了是程序可读性强,可以合理的使用空格 。
好了,本文就到这里,在以后如果在使用 Shell 脚本的时候有新的注意事项,文章会保持更新!
谢谢大家!
版权声明: 本文为 InfoQ 作者【矜辰所致】的原创文章。
原文链接:【http://xie.infoq.cn/article/1e982eb296024089baf5c1ca0】。文章转载请联系作者。
评论