3.1 shell的作用

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。也就是说,shell是用户与系统内核之间的接口程序。
shell也是命令解释器。
Shell 脚本(shell script),是一种为 shell 编写的脚本程序。shell 和 shell script 是两个不同的概念。我们是对shell脚本编程。
在本章中,shell是指linux的终端。即解释用户命令和shell程序的文字终端
image.png

3.2 shell程序的特点与用途

–shell程序可以认为是将shell命令按照控制结构组织到一个文本文件中,批量的交给shell去执行 。
–不同的shell解释器使用不同的shell命令语法。
–shell程序解释执行,不生成可以执行的二进制文件。
–可以帮助用户完成特定的任务,提高使用、维护系统的效率 。
–了解shell程序可以更好的配置和使用linux 。

Shell 编程跟 JavaScript、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。我们关注的是 Bash,也就是 Bourne Again Shell,由于易用和免费,Bash 在日常工作中被广泛使用。同时,Bash 也是多数Linux 系统默认的 Shell。(/bin/bash)

3.3 基于Bash的Shell程序设计

3.3.1 简单程序示例

image.png

3.3.2 程序编辑与运行过程:

  1. 使用vim编辑,保存文件
  2. 将文件赋予可以执行的权限(chmod +x ./test.sh)

(这里使用./test.sh是因为和运行其他二进制程序一样,如果直接写test.sh,linux系统会直接在PATH中寻找有没有叫test.sh的,但是只有/usr/bin或/bin在PATH里面,你的当前目录通常不在PATH里面,所以写成test.sh会找不到命令的,而是用./test.sh告诉系统就在当前目录找)

  1. 运行及试错(./test.sh)

    3.3.3 一般结构

    image.png

    3.3.4 shell变量声明与使用

    声明变量:

    shell变量是弱类型的,声明变量不用声明类型。变量可以存储不同类型的内容,因此使用起来比较灵活。
    变量使用时要明确变量的类型,并区分大小写。
    变量命名只能使用英文字母,数字和下划线,首个字母不能以数字开头。
    格式:
    变量=值(注意:等号两侧不能有空格
    a=”hello”
    b=8

    使用变量:

    使用一个定义过的变量,只需要在变量名前加美元符号$即可。
    例如:
    your_name=”xiaoming”
    echo $your_name
    echo ${your_name}
    变量名外面的花括号是可选的,加不加都行。加花括号是为了帮助解释器识别变量的边界。
    推荐给所有变量加上花括号,这是一个好的编程习惯。
    已经定义的变量也可以被重新定义,但重新定义时不能写作:
    $your_name=”daming” #这样写是错误的my
    因为只有在使用变量时才加上美元符$。

    只读变量:

    用readonly命令将变量定义为只读变量,只读变量的值不能改变。
    image.png
    image.png

    删除变量:

    image.png

chmod +x somefile 和 chmod a+x somefile 是一样的

3.3.5 shell数组

bash支持一维数组,但不支持多维数组。且不限制数组的长度。
类似于C语言,数组元素的下标从0开始编号。

定义数组:

array_name=(value0 value1 value2)
还可以单独定义数组的各个分量:
array_name[0]=value0;
array_name[1]=value1;

读取数组:

value3=${array_name[3]}
使用@符号可以获取数组中的所有元素:
echo ${array_name[@]} #使用@ 或 * 可以获取数组中的所有元素
image.png

获取数组长度:

获取元素个数
length=${#array_name[@]}
#获取单个元素长度
lengthn=${#array_name[n]}
image.png

3.3.6 shell字符串

shell字符串可以用单引号也可用双引号,也可以不用加引号。

单引号

单引号中的任何字符都会原样输出,因此单引号字符串中的变量无效。
str=’this is a string’

双引号

双引号的优点:
双引号里面可以有变量,双引号里面可以出现转义字符。

your_name=”xiaoming”
str=”hello,i know you are \”$your_name\”! \n” #!和\n之间一定要加空格
echo -e $str
#-e:激活转义字符。使用-e选项时,若字符串中出现以下字符,则特别加以处
理,而不会将它当成一般文字输出:
输出结果为:
hello, i know you are “xiaoming”!
image.png

拼接字符串

image.png
image.png
需要注意的是,以上脚本中`为反引号,而不是单引号’,不要弄错。因为expr表达式需要用 ``反引号括住。

3.3.7 shell传递参数

在执行shell样本时,我们可以向脚本传递参数,获取参数的格式为:$n。n代表数字,1为执行脚本的第一个参数,2位执行脚本的第二个参数········
下例将展示向脚本传递3个参数;其中$0为执行脚本的文件名,包含文件路径:
image.png
echo “参数个数为:$#”;
echo “传递的参数作为一个字符串显示:$*”;
再为脚本设置可执行权限,并执行脚本,输出结果如下所示:

  1. $ chmod +x test.sh
  2. $ ./test.sh 1 2 3
  3. Shell 传递参数实例!
  4. 第一个参数为:1
  5. 第二个参数为:2
  6. 第三个参数为:3
  7. 参数个数为:3
  8. 传递的参数作为一个字符串显示:1 2 3

$0为执行脚本的函数名;
$1是传递给函数的第1个参数 ;
$#为传递给函数的参数个数;
$?显示最后命令的退出状态。0表示没有错误 ,其他值表示有错误。
$*和$@为传递给函数的所有参数

  • $*把所有参数作为一个整体,以一个单字符串的形式显示所有向脚本传递的参数。
  • $@把所有参数看成一个参数数组。

    $*和$@的区别

    相同点:都是引用所有参数
    不同点:只有在双引号中体现出来。假设在脚本运行中写了三个参数1、2、3,则 * 等价于“1 2 3”(传递了一个参数);而 @ 等价于“1”“2”“3”(传递了三个参数) ```shell

    !/bin/bash

    echo “— \$ 演示 —-“ for i in “$“; do echo $i done

echo “— \$@ 演示 —-“ for i in “$@”; do echo $i done

  1. 执行脚本,输出结果如下所示:
  2. ```shell
  3. $ chmod +x test.sh
  4. $ ./test.sh 1 2 3
  5. -- $* 演示 ---
  6. 1 2 3
  7. -- $@ 演示 ---
  8. 1
  9. 2
  10. 3

3.3.8 简单数学表达式

Shell 和其他编程语言一样,支持多种运算符,包括:

  • 算数运算符
  • 关系运算符
  • 布尔运算符
  • 字符串运算符
  • 文件测试运算符

    expr命令

    原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
    expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
    例如:两数相加

    1. #!/bin/bash
    2. val=`expr 2 + 2`
    3. echo "两数之和为:$val"

    运行结果为:
    两数之和为:4
    注意:

  • 表达式与运算符之间要有空格,例如2+2是不对的,必须写成2 + 2;

  • 完整的表达式要被 包含,这个符号叫反引号,在esc键下面。

image.png

let命令

例子:计算(2+3)4的值
let s=(2+3)
4
说明:

  • 与expr指令相比,let命令更简洁直观;
  • 当运算符中有<、>、&、|等时,同样需要使用单引号,双引号或斜杠修饰运算符。

    条件表达式

image.png

  1. #条件表达式一定要放在方括号内,并且要有空格,例如[$a==$b]是错误的,必须写成[$a == $b].
  2. if [$a == $b]
  3. then
  4. echo "a 等于 b"
  5. fi
  6. if [$a != $b]
  7. then
  8. echo "a 不等于 b"
  9. fi

关系运算符

假设a为10,b为20:
image.png

  1. #!/bin/bash
  2. a=10
  3. b=20
  4. if [ $a -eq $b ]
  5. then
  6. echo "$a -eq $b : a 等于 b"
  7. else
  8. echo "$a -eq $b: a 不等于 b"
  9. fi
  10. if [ $a -ne $b ]
  11. then
  12. echo "$a -ne $b: a 不等于 b"
  13. else
  14. echo "$a -ne $b : a 等于 b"
  15. fi
  16. if [ $a -gt $b ]
  17. then
  18. echo "$a -gt $b: a 大于 b"
  19. else
  20. echo "$a -gt $b: a 不大于 b"
  21. fi
  22. if [ $a -lt $b ]
  23. then
  24. echo "$a -lt $b: a 小于 b"
  25. else
  26. echo "$a -lt $b: a 不小于 b"
  27. fi
  28. if [ $a -ge $b ]
  29. then
  30. echo "$a -ge $b: a 大于或等于 b"
  31. else
  32. echo "$a -ge $b: a 小于 b"
  33. fi
  34. if [ $a -le $b ]
  35. then
  36. echo "$a -le $b: a 小于或等于 b"
  37. else
  38. echo "$a -le $b: a 大于 b"
  39. fi

执行脚本结果为:

  1. 10 -eq 20: a 不等于 b
  2. 10 -ne 20: a 不等于 b
  3. 10 -gt 20: a 不大于 b
  4. 10 -lt 20: a 小于 b
  5. 10 -ge 20: a 小于 b
  6. 10 -le 20: a 小于或等于 b

布尔运算符与文件测试运算符

image.png

由此我们可以结合逻辑运算符,对多个文件的属性或一个文件的多重属性进行判断:

  1. #逻辑与-a:condition_1 a condition_2
  2. [-d dir1 -a -w dir1]
  3. echo $?
  4. #$? 上个命令的退出状态,或函数的返回值。
  5. #例子1 :
  6. #ls 命令没有找到匹配的结果. 所以返回2 $? 就是2
  7. #[root@sg-rhel6-17 etc]# ls /tmp/*.log
  8. #ls: cannot access /tmp/*.log: No such file or directory
  9. #[root@sg-rhel6-17 etc]# echo $?
  10. #2
  11. #例子2 :
  12. #ls 命令找到了结果. 成功返回0 所以$? 就是0
  13. #[root@sg-rhel6-17 etc]# ls /tmp/*.tmp
  14. #/tmp/reminder.tmp
  15. #[root@sg-rhel6-17 etc]# echo $?
  16. #0
  17. #逻辑或-o:condition_1 o condition_2
  18. [ -x file1 o r file2 ]
  19. 逻辑非!:!condition_1

执行脚本,输出结果如下所示:

  1. 文件可读
  2. 文件可写
  3. 文件可执行
  4. 文件为普通文件
  5. 文件不是个目录
  6. 文件不为空
  7. 文件存在

在shell中,条件为真可以用0代替;条件为假可以用非零代替。

逻辑运算符

假定变量 a 为 10,变量 b 为 20:
image.png

  1. #!/bin/bash
  2. a=10
  3. b=20
  4. if [[ $a -lt 100 && $b -gt 100 ]]
  5. then
  6. echo "返回 true"
  7. else
  8. echo "返回 false"
  9. fi
  10. if [[ $a -lt 100 || $b -gt 100 ]]
  11. then
  12. echo "返回 true"
  13. else
  14. echo "返回 false"
  15. fi

执行脚本,输出结果如下所示:

  1. 返回 false
  2. 返回 true

字符串运算符

假定变量 a 为 “abc”,变量 b 为 “efg”:
image.png
image.png

  1. #!/bin/bash
  2. a="abc"
  3. b="efg"
  4. if [ $a = $b ]
  5. then
  6. echo "$a = $b : a 等于 b"
  7. else
  8. echo "$a = $b: a 不等于 b"
  9. fi
  10. if [ $a != $b ]
  11. then
  12. echo "$a != $b : a 不等于 b"
  13. else
  14. echo "$a != $b: a 等于 b"
  15. fi
  16. if [ -z $a ]
  17. then
  18. echo "-z $a : 字符串长度为 0"
  19. else
  20. echo "-z $a : 字符串长度不为 0"
  21. fi
  22. if [ -n "$a" ]
  23. then
  24. echo "-n $a : 字符串长度不为 0"
  25. else
  26. echo "-n $a : 字符串长度为 0"
  27. fi
  28. if [ $a ]
  29. then
  30. echo "$a : 字符串不为空"
  31. else
  32. echo "$a : 字符串为空"
  33. fi

执行脚本,输出结果如下所示:

  1. abc = efg: a 不等于 b
  2. abc != efg : a 不等于 b
  3. -z abc : 字符串长度不为 0
  4. -n abc : 字符串长度不为 0
  5. abc : 字符串不为空

3.3.9 shell的test命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

数值测试:

image.png

  1. num1=100
  2. num2=100
  3. if test $[num1] -eq $[num2]
  4. then
  5. echo '两个数相等!'
  6. else
  7. echo '两个数不相等!'
  8. fi

输出结果:
两个数相等!

代码中的 [] 执行基本的算数运算,如:

  1. #!/bin/bash
  2. a=5
  3. b=6
  4. result=$[a+b] # 注意等号两边不能有空格
  5. echo "result 为: $result"

结果为:
result 为: 11

字符串测试:

image.png

  1. num1="ru1noob"
  2. num2="runoob"
  3. if test $num1 = $num2
  4. then
  5. echo '两个字符串相等!'
  6. else
  7. echo '两个字符串不相等!'
  8. fi

输出结果:
两个字符串不相等!

文件测试:

image.png

  1. cd /bin
  2. if test -e ./bash
  3. then
  4. echo '文件已存在!'
  5. else
  6. echo '文件不存在!'
  7. fi

输出结果:
文件已存在!

image.png
3.3.10 shell流程控制

控制结构就是根据某个条件的判断结果,改变程序执行的路径。可以简单的将控制结构分为分支和循环两种 。

if else

  1. if condition
  2. then
  3. command1
  4. command2
  5. ...
  6. commandN
  7. fi
  1. if condition
  2. then
  3. command1
  4. command2
  5. ...
  6. commandN
  7. else
  8. command
  9. fi
  1. if condition1
  2. then
  3. command1
  4. elif condition2
  5. then
  6. command2
  7. else
  8. commandN
  9. fi

以下实例用于判断两个变量是否相等:

  1. a=10
  2. b=20
  3. if [ $a == $b ]
  4. then
  5. echo "a 等于 b"
  6. elif [ $a -gt $b ]
  7. then
  8. echo "a 大于 b"
  9. elif [ $a -lt $b ]
  10. then
  11. echo "a 小于 b"
  12. else
  13. echo "没有符合的条件"
  14. fi

输出结果:
a 小于 b
image.png

for循环

下例顺序输出当前列表中的数字:

  1. for loop in 1 2 3 4 5
  2. do
  3. echo "The value is: $loop"
  4. done

输出结果:
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5

顺序输出字符串中的字符:

  1. #!/bin/bash
  2. for str in This is a string
  3. do
  4. echo $str
  5. done

输出结果:
This
is
a
string

while语句

while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。
以下是一个基本的 while 循环,测试条件是:如果 int 小于等于 5,那么条件返回真。int 从 1 开始,每次循环处理时,int 加 1。运行上述脚本,返回数字 1 到 5,
然后终止。

  1. #!/bin/bash
  2. int=1
  3. while(( $int<=5 ))
  4. do
  5. echo $int
  6. let "int++"
  7. done

运行脚本,输出:
1
2
3
4
5
while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按结束循环。

  1. echo '按下 <CTRL-D> 退出'
  2. echo -n '输入你最喜欢的网站名: '
  3. while read FILM
  4. do
  5. echo "是的!$FILM 是一个好网站"
  6. done

运行脚本,输出类似下面:
按下 退出
输入你最喜欢的网站名:菜鸟教程
是的!菜鸟教程 是一个好网站

image.png

until循环

until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
以下实例我们使用 until 命令来输出 0 ~ 9 的数字:

  1. #!/bin/bash
  2. a=0
  3. until [ ! $a -lt 10 ]
  4. do
  5. echo $a
  6. a=`expr $a + 1`
  7. done

输出结果为:
0
1
2
3
4
5
6
7
8
9

case···esac分支

ase … esac 为多选择语句,与其他语言中的 switch … case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case … esac 语句,esac(就是 case 反过来)作为结束标记。
可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
ase 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
下面的脚本提示输入 1 到 4,与每一种模式进行匹配:

  1. echo '输入 1 到 4 之间的数字:'
  2. echo '你输入的数字为:'
  3. read aNum
  4. case $aNum in
  5. 1) echo '你选择了 1'
  6. ;;
  7. 2) echo '你选择了 2'
  8. ;;
  9. 3) echo '你选择了 3'
  10. ;;
  11. 4) echo '你选择了 4'
  12. ;;
  13. *) echo '你没有输入 1 到 4 之间的数字'
  14. ;;
  15. esac
  1. 输入不同的数字,会有不同的结果:<br />输入 1 4 之间的数字: <br />你输入的数字为: 3<br /> 你选择了 3<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1054933/1631870588193-897b9ace-5ba0-410d-a432-22c25936f92a.png#clientId=u0d4fbe9d-5bcf-4&from=paste&height=322&id=u6d1cc72e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=430&originWidth=371&originalType=binary&ratio=1&size=18493&status=done&style=none&taskId=uf58b02e0-e87d-4b9c-b646-90501a1d238&width=278)

跳出循环

image.png
image.png