shell的基本概念
shell是什么?
壳与封装
shell就是壳的意思,在计算机中经常提到shell是用户操作接口的意思。
因为计算机程序本身很复杂,里面的实现和外面的调用必须分开。接口本身就是对内部复杂的实现的一种封装,外部只需要通过接口就可以很容易的实现效果,但是却不用理会内部实现的复杂性和原理。
程序或操作系统的用户接口
操作系统运行起来后都会给用户提供一个操作界面,这个操作界面就叫shell。用户可以通过shell来调用操作系统内部的复杂实现。
shell编程就是在shell层次上进行编程。譬如linux中的脚本编程、windows中的批处理。
两种shell:GUI和cmdline
GUI(图形用户界面),特点是操作简单、易学易用,适合使用电脑来工作的人。
cmdline(命令行界面),譬如linux的终端和windows的cmd,特点是不易用易学,优点是可以进行方便的shell编程,适合做开发的人。
展望:将来的shell应该是声音图像等接口的。
shell的运行原理:由消息接收、解析、执行构成的死循环
我们主要分析命令行shell的运行原理。
命令行shell其实就是一个死循环。这个死循环包含3个模块,这3个模块是串联的,分别是命令接收、命令解析、命令执行。
命令行有一个标准命令集,用户在操作的时候必须知道自己想要的操作用通过哪个命令来实现,不能随便输入命令。如果用户输入了一个不是标准命令的命令(不能识别的命令),提示用户这不是一个合法命令,然后重新回到命令行让用户输入下一个命令。
用户输入命令的界面是一个命令行,命令行的意思就是用户输入的命令是以行为单位的,更好理解的说用户输入的命令在用户按下回车键之后就算是结束了,shell可以开始接收了。
shell举例:uboot、linux终端、Windows图形界面等
常见的shell,uboot就是一个裸机程序构成的shell,clinux中断和windows的cmd是操作系统下的命令行shell。windows图形界面、ubuntu图形界面、android的图形界面这些都是图形界面的shell。突然想到另一个类型的shell,网页类型的shell,典型代表就是路由器。
shell是一类编程语言
编写shell脚本时使用的语言就是shell语言,又叫脚本语言。
shell脚本其实是一类语言而不是一个语言。
常用shell语言:sh、bash、csh、ksh、perl、python等
在linux下常用的脚本语言其实就是bash、sh;
perl、python这样的高级shell脚本语言,常用在网络管理配置等领域,系统运维人员一般要学习这些。
脚本语言一般在嵌入式中应用,主要是用来做配置。(一个复杂的嵌入式程序都是可配置的,配置过程就是用脚本语言来实现的)自然不会使用过于复杂的脚本语言特性,因此只需要针对性的学习即可。
linux下最常用的脚本就是bash。
shell脚本的运行机制:解释运行
C语言(C++)这种编写过程是:编写出源代码(源代码是不能直接运行的)然后编译链接形成可执行二进制程序,然后才能运行;而脚本程序不同,脚本程序编写好后源代码即可直接运行(没有编译链接过程)
shell程序是解释运行的,所谓解释运行就是说当我们执行一个shell程序时,shell解析器会逐行的解释shell程序代码,然后一行一行的去运行。(顺序结构)
CPU实际只认识二进制代码,根本不认识源代码。脚本程序源代码其实也不是二进制代码,CPU也不认识,也不能直接执行。只不过脚本程序的编译链接过程不是以脚本程序源代码为单位进行的,而是在脚本运行过程中逐行的解释执行时才去完成脚本程序源代码转成二进制的过程(不一定是编译链接,因为这行脚本程序可能早就编译连接好了,这里我们只是调用它)的。
编辑器、编译器、运行方法(脚本的3种执行方法)
shell程序是文本格式的,只要是文本编辑器都可以。但是因为我们的shell是要在linux系统下运行的,所以换行符必须是'\n',而windows下的换行符是"\r\n",因此windows中的编辑器写的shell不能在linux下运行。
编译器不涉及,因为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]传给他运行。
shell编程
shell程序和解释
shell程序的第一行一般都是: #!/bin/sh这行话以#!开始,后面加上一个pathname,这行话的意思就是指定shell程序执行时被哪个解释器解释执行。所以我们这里写上/bin/sh意思就是这个shell将来被当前机器中/bin目录下的sh可执行程序执行。
可以将第一行写为:#!/bin/bash来指定使用bash执行该脚本。
注意:在ubuntu上面默认使用的解释器sh其实不是bash,而是dash。dash是ubuntu中默认使用的脚本解释器。
脚本中的注释使用#,#开头的行是注释行。如果有多行需要注释,每行前面都要加#。(#就相当于是C语言中的//)
shell程序的正文,由很多行shell语句构成。
shell并不神秘
shell就是把以前命令行中键入执行的命令写成了程序。shell其实就是为了避免反复的在命令行下手工输入而发明的一种把手工输入步骤记录下来,然后通过执行shell脚本程序就能再次复述原来记录的手工输入过程的一种技术。
shell编辑完可以直接运行(不需编译)
shell中的变量定义和引用
变量定义和初始化。shell是弱类型语言(语言中的变量如果有明确的类型则属于强类型语言;变量没有明确类型就是弱类型语言),和C语言不同。在shell编程中定义变量不需要制定类型,也没有类型这个概念。
变量定义时可以初始化,使用=进行初始化赋值。在shell中赋值的=两边是不能有空格的。
注意:shell对语法非常在意,非常严格。很多地方空格都是必须没有或者必须有,而且不能随意有没有空格。
变量赋值,变量定义后可以再次赋值,新的赋值会覆盖老的赋值。shell中并不刻意区分变量的定义和赋值,反正每个变量就是一个符号,这个符号的值就是最后一个给他赋值时的值。
变量引用。shell中引用一个变量必须使用$符号,$符号就是变量解引用符号。
注意:$符号后面跟一个字符串,这个字符串就会被当作变量去解析。如果这个字符串本身没有定义,执行时并不会报错,而是把这个变量解析为空。也就是说在shell中没有被定义的变量其实就相当于是一个定义并赋值为空的变量。
注意:变量引用的时候可以$var,也可以${var}。这两种的区别是在某些情况下只能用${var}而不能简单的$var
var="hello"
echo "$varyou"
#这两条语句不会像预想的一样输出helloyou,而是输出空,因为不加{}则会把varyou认为是一个变量,而这个变量并未初始化,要想输出helloyou,则需要执行以下语句
var="hello"
echo "${var}you"
shell中无引用、单引号和双引号的区别
shell中使用字符串可以不加双引号,直接使用。而且有空格时也可以,但是缺陷是不能输出"或者其他转义字符。
shell中也可以使用单引号来表示字符串,也是直接使用的,不能输出转义字符。
单引号中:完全字面替换(不可包含单引号本身)
双引号中:
$加变量名可以取变量的值
反引号仍表示命令替换
\$表示$的字面值输出$符号
\`表示`的字面值
\"表示"的字面值
\\表示\的字面值
除以上情况之外,在其它字符前面的\无特殊含义,只表示字面值。
echo i am handsome
echo 'i am handsome'
echo "i am handsome"
# 以上三条语句执行结果相同,都为输出字符串i am handsome
str="very"
echo i am $str handsome
echo 'i am $str handsome'
echo "i am $str handsome"
# 以上第一条语句和第三条语句都输出i am very handsome,第二条语句输出i am $str handsome,由此可见,加''后会将其中的所有符号原封不动的输出
shell中调用linux命令
直接执行
反引号括起来执行。有时候我们在shell中调用linux命令是为了得到这个命令的返回值(结果值),这时候就适合用一对反引号 `` 来调用执行命令。
shell中的选择分支结构
shell的if语言用法很多,在此只介绍常用的,其他感兴趣可以自己去学
典型if语言格式
if [表达式]; then # 若表达式为true,则执行then后的语句
xxx
yyy
zzz
else # 若表达式为false,则执行else后的语句
xxx
ddd
uuu
fi
#注:表达式与[]左右必须有空格间隔,比如以下程序,写成["abc" = "afc"]则不会报错
if [ "abc" = "afc" ]; then # shell语法中比较字符串是否相等不用"==",而是"="
echo "equal"
else
echo "not equal"
fi
if的典型应用
判断文件是否存在。(-f),注意[]里面前后都有空格,不能省略。
判断目录是否存在 (-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表达式相结合
str="1"
[ -z $str ] || echo "false"
上面程序,根据短路原则,当||前的条件为真时,||后的语句就会直接跳过,不执行,此时就不会输出false,而当||前的条件为假时,||后的语句就会执行。
str="1"
[ -z $str ] && echo "false"
根据逻辑与的短路原则,当&&前的条件为真时,&&后的语句才会执行,反之则不执行。
shell中的循环结构
for循环
能看懂、能改即可。不要求能够完全不参考写出来。毕竟嵌入式并不需要完全重新手写shell,系统管理员(服务器运维人员,应用层系统级管理开发的才需要完全掌握shell)
for i in `ls`
do
echo $i
done
while循环
和C语言的循环在逻辑上无差别
要注意很多格式要求,譬如:while后面的[]两边都有空格,[]后面有分号分号(如果do放在一行的话),i++的写法中有两层括号。
i=1
j=11
while [ $i -lt $j ]
do
echo $i
i=$(($i + 1))
done
echo的创建和追加输入文件
(1)在shell中可以直接使用echo指令新建一个文件,并且将一些内容传入这个文件中。创建文件并输入内容的关键就是>。
(2)还可以使用echo指令配合追加符号>> 向一个已经存在的文件末尾追加输入内容。
shell中其他值得关注的知识点
case语句
shell中的case语句和C语言中的switch case语句作用一样,格式有差异
shell中的case语句天生没有break,也不需要break,和C语言中的switch case不同。shell中的case默认就是匹配上哪个执行哪个,不会说执行完了还去执行后面的其他case(就好像shell中的case语言默认都带了break)。
var=1
case $var in
1) echo "1" ;;
2) echo "2" ;;
esac
调用shell程序的传参
C语言中可以通过main函数的argc和argv给程序传参
shell程序本身也可以在调用时传参给他。在shell程序内部使用传参也是使用的一些特定符号来表示的,包括:
$#表示调用该shell时传参的个数。($#计数时只考虑真正的参数个数)
$0、$1、$2·····则依次表示传参的各个参数。
C语言:./a.out aa bb cc argc = 4, argv[0] = ./a.out, argv[1]是第一个有效参数
shell:source a.sh aa bb cc $# = 3, $0是执行这个shell程序的解析程序的名字,$1是第一个有效参数的值,$2是第2个有效参数的值·····
while循环和case语言和传参结合
shell中的break关键字和C语言中意义相同(都是跳出)但是用法不同。因为shell中case语句默认不用break的,因此在shell中break只用于循环跳出。所以当while中内嵌case语句时,case中的break是跳出外层的while循环的,不是用来跳出case语句的。
shell中的$# $1等内置变量的值不是不可变的,而是可以被改变,被shift指令改变。shift指令有点像左移运算符,把我们给shell程序的传参左移了一个移出去了,原来的$2变成了新的$1,原来的$#少了1个。
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done