Author: 想文艺一点的程序员
自动化专业 工科男
再坚持一点,再自律一点
CSDN@想文艺一点的程序员
来自朱有鹏嵌入式的学习笔记
文章目录
一、shell介绍
注意区分: shell 和 shell 脚本程序。
1、shell是操作系统的终端命令行
(1)shell可以理解为软件系统提供给用户操作的命令行界面,可以说它是人机交互的一种方式。
(2)我们可以使用shell和操作系统、uboot等软件系统进行交互。具体来说就是我们通过shell给软件系统输入命令然后回车执行,执行完成后又会回到shell命令行可以再次输入命令执行。
上述的操作方式一般情况下工作很好,但是有缺陷。譬如我们要在linux下创建一个文件a.c,可以touch a.c 但是如果我现在是用在linux下创建100个文件,分别为a1.c a2.c…a100.c 如果这时候还是手工去命令行下执行命令创建也可以,但是很累。最好的做法就是把创建过程写成一个shell脚本程序,然后去执行这个shell脚本程序,执行这个程序的效果和手工在命令行输入那些命令效果一样的。(回忆在arm裸机中安装交叉编译工具链时,创建arm-linux-xxx的符号链接时)
2、shell是一类编程语言
(1)编写shell脚本时使用的语言就是shell语言,又叫脚本语言。
(2)shell脚本其实是一类语言而不是一个语言。
3、常用shell语言:sh、bash、csh、ksh、perl、python等
(1)在linux下常用的脚本语言其实就是bash、sh;
(2)perl、python这样的高级shell脚本语言,常用在网络管理配置等领域,系统运维人员一般要学习这些。
(3)脚本语言一般在嵌入式中应用,主要是用来做配置。(一个复杂的嵌入式程序都是可配置的,配置过程就是用脚本语言来实现的)自然不会使用过于复杂的脚本语言特性,因此只需要针对性的学习即可。
(4)linux下最常用的脚本就是bash,我们学习也是以bash为主。
4、shell脚本的运行机制:解释运行
(1)C语言(C++)这种编写过程是:编写出源代码(源代码是不能直接运行的)然后编译链接形成可执行二进制程序,然后才能运行;而脚本程序不同,脚本程序编写好后源代码即可直接运行(没有编译链接过程)
(2)shell程序是解释运行的,所谓解释运行就是说当我们执行一个shell程序时,shell解析器会逐行的解释shell程序代码,然后一行一行的去运行。(顺序结构)
(3)CPU实际只认识二进制代码,根本不认识源代码。脚本程序源代码其实也不是二进制代码,CPU也不认识,也不能直接执行。只不过脚本程序的编译链接过程不是以脚本程序源代码为单位进行的,而是在脚本运行过程中逐行的解释执行时才去完成脚本程序源代码转成二进制的过程(不一定是编译链接,因为这行脚本程序可能早就编译连接好了,这里我们只是调用它)的。
二、动手写第一个shell
1、编辑器、编译器、运行方法(脚本的3种执行方法)
-
shell程序是文本格式的,只要是文本编辑器都可以。但是因为我们的shell是要在linux系统下运行的,所以换行符必须是’\n’,而windows下的换行符是"\r\n",因此windows中的编辑器写的shell不能在linux下运行。所以我们整个课程都是在linux下使用vi编辑器(实际上是vim)进行编写调试的。
-
编译器 不涉及,因为shell是解释性语言,直接编辑完就可以运行。
-
shell程序运行的运行有多种方法,这里介绍三种方法:
第一种:./xx.sh
,和运行二进制可执行程序方法一样。这样运行shell要求shell程序必须具有可执行权限。chmod a+x xx.sh来添加可执行权限。
第二种:source xx.sh
,source是linux的一个命令,这个命令就是用来执行脚本程序的。这样运行不需要脚本具有可执行权限。
第三种:bash xx.sh
,bash是一个脚本程序解释器,本质上是一个可执行程序。这样执行相当于我们执行了bash程序,然后把xx.sh作为argv[1]传给他运行。
2、hello world程序和解释
-
shell程序的第一行一般都是:
#!/bin/sh
这行话以#!开始,后面加上一个pathname,这行话的意思就是指定shell程序执行时被哪个解释器解释执行。所以我们这里写上/bin/sh意思就是这个shell将来被当前机器中/bin目录下的sh可执行程序执行。
可以将第一行写为:#!/bin/bash来指定使用bash执行该脚本。
注意:在ubuntu上面默认使用的解释器sh其实不是bash,而是dash。dash是ubuntu中默认使用的脚本解释器。
-
脚本中的注释使用#,#开头的行是注释行。如果有多行需要注释,每行前面都要加#。(#就相当于是C语言中的//)
-
shell程序的正文,由很多行shell语句构成。
echo “hello world.”
ls -l
3、shell并不神秘
(1)shell就是把以前命令行中键入执行的命令写成了程序。shell其实就是为了避免反复的在命令行下手工输入而发明的一种把手工输入步骤记录下来,然后通过执行shell脚本程序就能再次复述原来记录的手工输入过程的一种技术。
(2)shell编辑完可以直接运行(不需编译)
三、shell编程学习
1、shell中使用linux命令
(1)练习1:当前目录下创建文件a.txt
(2)练习2:当前目录下创建文件夹dir,dir下创建文件b.txt
直接调用命令
利用反引号(ESC下面那个符号)来执行
将我们的linux命令 用单引号来括起来
MYPATH="`pwd`/include"
echo "MYPATH= $MYPATH"
总结:以上2个练习的目的是让大家基本学会写脚本,明白脚本编程其实就是把以前在命令行下输入的命令挪到脚本程序中去然后一次执行。
2、shell中的变量定义和引用
# varible define and use
string="hello world"
echo $string
# 赋值
string="new string"
-
变量定义和初始化。shell是弱类型语言(语言中的变量如果有明确的类型则属于强类型语言;变量没有明确类型就是弱类型语言),和C语言不同。在shell编程中定义变量不需要制定类型,也没有类型这个概念。
变量定义时可以初始化,使用=进行初始化赋值。在shell中赋值的=两边是不能有空格的。
注意:shell对语法非常在意,非常严格。很多地方空格都是必须没有或者必须有,而且不能随意有没有空格。 -
变量赋值,变量定义后可以再次赋值,新的赋值会覆盖老的赋值。shell中并不刻意区分变量的定义和赋值,反正每个变量就是一个符号,这个符号的值就是最后一个给他赋值时的值。
string="hello world"
string="new string"
- 变量引用。shell中引用一个变量必须使用 符 号 , 符号, 符号,符号就是变量解引用符号。
string="new string"
echo $string
注意:$符号后面跟一个字符串,这个字符串就会被当作变量去解析。
echo string
此处这个字符串本身没有定义,执行时并不会报错,而是把这个变量解析为空。也就是说在shell中没有被定义的变量其实就相当于是一个定义并赋值为空的变量。
3、shell中无引用、单引号和双引号的区别
(1)shell中使用字符串可以不加双引号,直接使用。而且有空格时也可以,但是缺陷是不能输出"或者其他转义字符。
(2)shell中也可以使用单引号来表示字符串,也是直接使用的,不能输出转义字符。
(3)单引号中:完全字面替换(不可包含单引号本身)
(4)双引号中:
$加变量名可以取变量的值
反引号仍表示命令替换
$表示 的 字 面 值 输 出 的字面值 输出 的字面值输出符号
`表示`的字面值
"表示"的字面值
\表示\的字面值
除以上情况之外,在其它字符前面的\无特殊含义,只表示字面值。
四、shell中的选择分支结构
shell的if语言用法很多,在此只介绍常用的,其他感兴趣可以自己去学
- 典型if语言格式
if [表达式]; then
xxx
yyy
zzz
else
xxx
ddd
uuu
fi
- if的典型应用
判断文件是否存在。(-f), 注意-f前面和路径后面都有空格,不能省略。
if [ -f a.txt ]; then
echo "yes!"
else
echo "no!,please touch."
touch a.txt
fi
判断目录是否存在 (-d)
判断字符串是否相等(“str1” = “str2”),注意用一个等号而不是两个
判断数字是否相等(-eq)、大于(-gt)、小于(-lt)、大于等于(-ge)、小于等于(-le) 回忆一下在ARM裸机中讲述ARM汇编条件执行时,曾经用过这些条件判断的缩写。(eq就是equal,gt就是greater than,lt就是less than,ge就是greater or equal,le就是less or equal)
判断字符串是否为空(-z)
注意-z判断时如果变量本身没定义也是不成立(也就是说-z认为没定义不等于为空)
- if判断式中使用“-o”表示逻辑或
相当于C语言中在if后面的条件式中用逻辑与、逻辑或来连接2个式子,最终的if中是否成立取决于2个式子的逻辑运算结果。
- 逻辑与&&和逻辑或||与 简写的if表达式相结合
它没有 if 关键字,如何理解:
首先用逻辑或来进行连接,在C语言当中的意义就是 “||” 前面的式子,如果是 真 ,后面就没有必要来执行了,如果前面是 假,后面才会执行。
(主要逻辑就是:前面为真,后面就不着来判断了,前面为假,才会接着后面去判断)
str="1"
[ -z $str ] || echo "fei kong" //echo "fei kong" 会执行,打印出fei kong
str=""
[ -z $str ] || echo "fei kong" // echo "fei kong" 不会执行
(主要逻辑就是:前面为假,后面就不着来判断了,前面为真,才会接着后面去判断)
str="1"
[ -z $str ] && echo "fei kong" //echo "fei kong" 不会执行
str=""
[ -z $str ] && echo "fei kong" // echo "fei kong" 会执行,打印出fei kong
五、shell中的选择分支
1、for循环
执行结果
2、while 循环
注意:
这里 i++ 变成了 i=$(($i + 1))
要注意很多格式要求,
譬如:while后面的[]两边都有空格,[]后面有分号分号(如果do放在一行的话),
i++的写法中有两层括号。
六、echo 的创建和追加输入文件
(1)在shell中可以直接使用echo指令新建一个文件,并且将一些内容传入这个文件中。创建文件并输入内容的关键就是>。
echo "#include <stdio.h>" > a.txt
// 解析:在当前目录创建 a.txt 文本,然后将 “ ”里面的东西写入到里面。
(2)还可以使用echo指令配合追加符号 >>
向一个已经存在的文件末尾追加输入内容。
#! /bin/sh
# echo
echo "#include <stdio.h>
int main(void)
{
return 0;
}" > a.c
echo "hello" >> a.c
执行结果
七、shell里面其他值得关注的点
1、case语句
(1)shell中的case语句和C语言中的switch case语句作用一样,格式有差异
(2)shell中的case语句天生没有break,也不需要break,和C语言中的switch case不同。shell中的case默认就是匹配上哪个执行哪个,不会说执行完了还去执行后面的其他case(就好像shell中的case语言默认都带了break)。
var=2 //先定义一个变量
case $var in // 固定格式
1) echo "1" // 半个括号代表它的值(最前面那个)
2) echo "2"
esac // 结束
2、调用shell程序的传参
- C语言中可以通过main函数的argc和argv给程序传参(详情参考《4.8.3.argc、argv与main函数的传参》)
举例:
mkx210 是当前目录下面的一个C 文件。
pwm.bin , 210.bin 就是给这个 C 文件里面的 main 去传参。
C语言:./a.out aa bb cc argc = 4, argv[0] = ./a.out, argv[1]是第一个有效参数····
char argc:表示传参的个数,
char **argc : 一个字符串二维数组,存放传进来的参数。
./mkx210 pwm.bin 210.bin
- shell程序本身也可以在调用时传参给他。在shell程序内部使用传参也是使用的一些特定符号来表示的,包括:
$#
表示调用该shell时传参的个数。($#计数时只考虑真正的参数个数)
$0、$1、$2
·····则依次表示传参的各个参数。
shell 脚本
# 演示使用 $# 和 $0 $1 来进行传参
打印我们所传的参数
echo $# $1 $2 $3
linux 下面执行操作
source a.sh aa bb cc
执行结果:
3 bash aa bb cc
$# = 3,
$0是执行这个shell程序的解析程序的名字,
$1是第一个有效参数的值,
$2是第2个有效参数的值·····
注意:shell中的很多语法特性和C语言中是相同的,也有很多是不同的。所以大家学的越多越容易混淆(本质原因还是用的不熟悉,用的少),解决方案是:做笔记、作总结、多写代码经常用
3、while循环和case语言和传参结合
理解这段程序:
1、 while 循环的理解:
[ $# -gt 0 ]
这个是判断条件,判断传参的个数是否 大于 0。(greater than)break
: 这里是特指跳出 while 循环,因为我们的 case 是直接省略break 的。
2、case 的理解:case "$1" in
: case 的固定格式,用来判断 变量 1 到底是哪一种形式。--)
版括号前面的就是 case1的情况,也就是 变量 1 等于--
的情况
3、传参的理解:
$# $1
这里涉及到了两个传参- shift 的用法: 相当于把我们的参数进行左移