嵌入式 Shell 基础

脚本语言

  • 常用的脚本语言有 sh、bash、csh、ksh、perl、python;
  • 在 Linux 下常用的脚本语言其实就是 bash、sh;
  • 脚本语言一般在嵌入式中应用,主要是用来做配置。(一个复杂的嵌入式程序都是可配置的,配置过程就是用脚本语言来实现的)自然不会使用过于复杂的脚本语言特性,因此只需要针对性的学习即可。

shell 脚本的运行机制

  • C/C++ 语言这种编写过程是:编写出源代码(源代码是不能直接运行的)然后编译链接形成可执行二进制程序,然后才能运行;而脚本程序不同,脚本程序编写好后源代码即可直接运行(没有编译链接过程);
  • shell 程序是解释运行的,所谓解释运行就是说当我们执行一个 shell 程序时,shell 解析器会逐行的解释 shell 程序代码,然后一行一行的去运行。(顺序结构)
  • CPU 实际只认识二进制代码,根本不认识源代码。脚本程序源代码其实也不是二进制代码,CPU 也不认识,也不能直接执行。只不过脚本程序的编译链接过程不是以脚本程序源代码为单位进行的,而是在脚本运行过程中逐行的解释执行时才去完成脚本程序源代码转成二进制的过程(不一定是编译链接,因为这行脚本程序可能早就编译连接好了,这里我们只是调用它)。

动手写第一个 shell

编辑器与编译器

  • shell 程序是文本格式的,只要是文本编辑器都可以。但是因为我们的 shell 是要在 Linux 系统下运行的,所以换行符必须是\n,而 Windows 下的换行符是\r\n,因此 Windows 中的编辑器写的 shell 不能在 Linux 下运行。
  • 编译器不涉及,因为 shell 是解释性语言,直接编辑完就可以运行。

shell 程序运行的运行的三种方法

  • ./xx.sh,和运行二进制可执行程序方法一样。这样运行 shell 要求 shell 程序必须具有可执行权限。chmod a+x xx.sh 来添加可执行权限。
  • source xx.shsource 是 Linux 的一个命令,这个命令就是用来执行脚本程序的。这样运行不需要脚本具有可执行权限。
  • bash xx.shbash 是一个脚本程序解释器,本质上是一个可执行程序。这样执行相当于我们执行了 bash 程序,然后把 xx.sh 作为 argv[1] 传给他运行。

hello world 程序和解释

  • shell 程序的第一行一般都是以#!/bin/sh开始,这行话的意思就是指定 shell 程序执行时被哪个解释器解释执行。所以我们这里写上/bin/sh意思就是这个shell将来被当前机器中/bin目录下的sh可执行程序执行。可以将第一行写为#!/bin/bash来指定使用bash执行该脚本。
  • 脚本中的注释使用##开头的行是注释行。如果有多行需要注释,每行前面都要加#。(#就相当于是 C 语言中的//);
  • shell 程序的正文,由很多行 shell 语句构成。

shell 语法

shell 就是把以前命令行中键入执行的命令写成了程序。shell 其实就是为了避免反复的在命令行下手工输入而发明的一种把手工输入步骤记录下来,然后通过执行 shell 脚本程序就能再次复述原来记录的手工输入过程的一种技术。

shell 中的变量定义和引用

  • 变量定义和初始化。shell 是弱类型语言(语言中的变量如果有明确的类型则属于强类型语言;变量没有明确类型就是弱类型语言),和 C 语言不同。在 shell 编程中定义变量不需要制定类型,也没有类型这个概念。
  • 变量定义时可以初始化,使用=进行初始化赋值。在 shell 中赋值的=两边是不能有空格的。
    注意:shell 对语法非常在意,非常严格。很多地方空格都是必须没有或者必须有,而且不能随意有没有空格。
  • 变量赋值,变量定义后可以再次赋值,新的赋值会覆盖老的赋值。shell 中并不刻意区分变量的定义和赋值,反正每个变量就是一个符号,这个符号的值就是最后一个给他赋值时的值。
  • 变量引用。shell 中引用一个变量必须使用$符号,$符号就是变量解引用符号。

注意:$符号后面跟一个字符串,这个字符串就会被当作变量去解析。如果这个字符串本身没有定义,执行时并不会报错,而是把这个变量解析为空。也就是说在 shell 中没有被定义的变量其实就相当于是一个定义并赋值为空的变量。

注意:变量引用的时候可以$var,也可以${var}。这两种的区别是在某些情况下只能用${var}而不能简单的$var

shell 中无引用、单引号和双引号的区别

  • shell 中使用字符串可以不加双引号,直接使用。而且有空格时也可以,但是缺陷是不能输出"或者其他转义字符。
  • shell 中也可以使用单引号来表示字符串,也是直接使用的,不能输出转义字符。
  • 单引号中:完全字面替换(不可包含单引号本身)
  • 双引号中:
    • $加变量名可以取变量的值
    • 反引号仍表示命令替换
    • \$表示$的字面值(输出$符号)
    • `表示`的字面值
    • \"表示"的字面值
    • \\表示\的字面值

除以上情况之外,在其它字符前面的\无特殊含义,只表示字面值。

单引号会原样输出,双引号可以调用命令:

PATH_A="`pwd`/include"
PATH_B='`pwd`/include'
echo $PATH_A
# /home/a/b/include
echo $PATH_B
# `pwd`/include

shell 中调用 Linux 命令

  • 直接执行
  • 反引号括起来执行。有时候我们在 shell 中调用 Linux 命令是为了得到这个命令的返回值(结果值),这时候就适合用一对反引号 (键盘上 ESC 按键下面的那个按键,和~在一个按键上) 来调用执行命令。

shell 中的选择分支结构

  • shell 的 if 语言用法很多,在此只介绍常用的,其他感兴趣可以自己去学

典型if语言格式:

if [ 表达式 ]; then
  xxx
  yyy
  zzz
else
  xxx
  ddd
  uuu
fi

if 的典型应用

  • -f判断文件是否存在,注意[]里面前后都有空格,不能省略

  • -d判断目录是否存在

  • "str1" = "str2"判断字符串是否相等,注意用一个等号而不是两个

  • 判断数字是否相等

    • -eq等于
    • -gt大于
    • -lt小于
    • -ge大于等于
    • -le小于等于
  • -z判断字符串是否为空,注意-z判断时如果变量本身没定义也是不成立(也就是说-z 认为没定义不等于为空)

  • -o表示逻辑或,连接两个表达式

    • if [ 10 -eq 10 -o ]; then
  • && ||表示逻辑与和逻辑或

shell 中的循环结构

for 循环,要求能看懂、能改即可。不要求能够完全不参考写出来。因为毕竟嵌入式并不需要完全重新手写。

while 循环,和 C 语言的循环在逻辑上无差别,要注意很多格式要求,譬如:while 后面的 [] 两边都有空格,[] 后面有分号(如果 do 放在一行的话),i++的写法中有两层括号。

echo 的创建和追加输入文件

  • 在 shell 中可以直接使用 echo 指令新建一个文件,并且将一些内容传入这个文件中。创建文件并输入内容的关键就是>
  • 还可以使用 echo 指令配合追加符号>> 向一个已经存在的文件末尾追加输入内容。

shell 中其他值得关注的知识点

case 语句

  • shell 中的 case 语句和 C 语言中的 switch case 语句作用一样,格式有差异
  • shell 中的 case 语句天生没有 break,也不需要break,和 C 语言中的 switch case 不同。shell 中的 case 默认就是匹配上哪个执行哪个,不会说执行完了还去执行后面的其他 case

调用 shell 程序的传参

  • C 语言中可以通过 main 函数的 argcargv 给程序传参
  • shell 程序本身也可以在调用时传参给他。在 shell 程序内部使用传参也是使用的一些特定符号来表示的,包括:
    • $#表示调用该 shell 时传参的个数。($#计数时只考虑真正的参数个数)
    • $0、$1、$2·····则依次表示传参的各个参数
./a.out aa bb cc     

# argc = 4
# argv[0] = ./a.out
# argv[1] 是第一个有效参数····
source a.sh aa bb cc  
# $# = 3
# $0是执行这个 shell 程序的解析程序的名字
# $1是第一个有效参数的值
# $2是第 2 个有效参数的值·····

while 循环和 case 语言和传参结合

  • shell 中的 break 关键字和 C 语言中意义相同(都是跳出)但是用法不同。因为 shell 中 case 语句默认不用 break 的,因此在 shellbreak 只用于循环跳出。所以当 while 中内嵌 case 语句时,case 中的 break 是跳出外层的 while 循环的,不是用来跳出 case 语句的。
  • shell 中的$# $1等内置变量的值是可以被改变,被 shift 指令改变。shift 指令有点像左移运算符,把我们给 shell 程序的传参左移了一个移出去了,原来的$2变成了新的$1,原来的$#少了 1 个。