简介

Shell是一个命令解释器,用户输入命令来获得自己想要的结果,但是终端中输入的命令很难进行高级语言的选择、循环等操作。不过Shell程序可以存放在文件上,称为Shell脚本(虽然Linux文件不以后缀名区分文件类型,但是一般编写Shell脚本时文件名会命名为以.sh结尾)。在脚本中可以较方便的进行类似高级语言的操作。

最简单的Shell脚本

05-Shell - 图1

我们都知道,直接在终端输入echo命令是回显参数,把echo命令放在shell脚本中有相同的效果。

设置成可执行文件

05-Shell - 图2

变量

Shell脚本中的变量直接使用=便可创建,使用$解析变量名。{}是分组命令,表示H是一个变量,这里不加也可以。

05-Shell - 图3

特殊变量

  • $#:除脚本名外,命令行上参数的个数。
  • $*:表示在命令行上实际给出的所有实参。
    • 如:exam3.sh A B C D E F G H I J K$#是11。$*是: A B C D E F G H I J K
  • $n:表示命令行上第n个参数
    • $0表示文件名 $1表示第一个参数 …
  • $@:表示在命令行上实际给出的所有实参。
    • 如:exam3.sh A B C D E F G H I J K$@就是: “A” “B” “C” “D” “E” “F” “G” “H” “I” “J” “K”
  • $$:当前进程的进程号
  • $!:上一个后台命令对应的进程号。
  • $?:上一条前台命令执行后的返回值。

算术运算

执行算术运算需要使用let,如let c=$a+$b。可以使用c=$(($a+$b))代替。其中算术运算符及优先级等同于C语言。同时多了个**表示幂运算。(运算符前后不要有空格)

05-Shell - 图4

从命令行读入参数

直接使用read,命令行中的参数会读到read后面跟的参数(相当于变量)里。

05-Shell - 图5

读入时输出提示信息:

05-Shell - 图6

引号

  • 双引号:由双引号引起来的字符(除$`和`)都被当做普通字符对待。`
    • $表示变量替换;
    • ` 表示命令替换;
    • \之后的字符只有是$`、`双引号`、换行符之一时会成为转义字符。其他情况都是\本身。

05-Shell - 图7

  • 单引号:单引号引起来的字符都是普通字符。特殊字符也失效。 05-Shell - 图8
  • 倒引号:被到引号引起来的字符被解释为命令。如上上图中所示。

数组

变量之间使用空格隔开各个元素。如果元素中有空格,使用双引号引起来。

05-Shell - 图9

测试条件

任何命令都可以作为条件,shell会执行这个命令并检查返回值,如果命令成功(返回值为0),表示真。

  • test <条件>:如test n1 -eq n2
  • [ 条件 ]:如[ n1 -eq n2 ]

有关文件方面的测试

  • -r 文件名:真 <==> 文件存在并且是用户可读
  • -w 文件名:真 <==> 文件存在并且是用户可写
  • -x 文件名:真 <==> 文件存在并且用户可执行
  • -f 文件名:真 <==> 文件存在且是普通文件
  • -d 文件名:真 <==> 文件存在且是目录文件
  • -s 文件名:真 <==> 文件存在且长度大于0

有关字符串方面的测试

  • -z s1:真 <==> 字符串长度为0
  • -n s1:真 <==> 字符串长度大于0
  • s1:真 <==> 字符串不是空字符串
  • s1 = s2(在“=”前后应有空格):真 <==> 字符串相等
  • s1 != s2:真 <==> 字符串不等
  • s1 < s2:真 <==> 按字典顺序s1在s2之后
  • s1 > s2:真 <==> 按字典顺序s1在s2之前

数值方面的测试

  • n1 -eq n2:真 <==> 数值相等
  • n1 -ne n2:真 <==> 数值不等
  • n1 -lt n2:真 <==> n1小于n2
  • n1 -le n2:真 <==> n1小于或等于n2
  • n1 -gt n2:真 <==> n1大于n2
  • n1 -ge n2:真 <==> n1大于或等于n2

逻辑运算符

  • !:逻辑非
  • -a:逻辑与
  • -o:逻辑或
  • (表达式):圆括号括起来表示为一条语句

选择结构

05-Shell - 图10

循环结构

05-Shell - 图11

break & continue

和C语言一致。

shift

参数跳转命令:不跟数组默认跳转1位,跟了跳转n位。

命令行 ex.sh A B C D E F
原位置参数 $0 $1 $2 $3 $4 $5 $6
移位后参数 $0 $1 $2 $3 $4 $5

还可以用于循环结构的done上面,表示每次选择指定参数。

参数置换变量

格式 var1为空 var1不空
var2=${var1:-str} var2=str。var1不变 var2=$var1。var1不变
var2=${var1:=str} var2=var1=str var2=$var1。var1不变
var2=${var1:+str} var2为空。var1不变 var2=str。var1不变
var2=${var1:?str} 输出:“shell 脚本名:var1:str”并退出shell。var2不变 var2=$var1。var1不变

ex1

编写ex1.sh,参数为一个大于 20 的正整数。先检查参数是否符合要求。如果不符合要求,请给出提示;如果符合要求,输出这个参数的平方。

05-Shell - 图12

ex2

编写ex2.sh,首先显示当天日期,然后查找给定的用户是否在系统中工作(who 命令)。如果在系统中,就输出一条欢迎语句(例如 hello,xxxx!);如果不在系统中,就输出一条语句(waiting for xxx!)

05-Shell - 图13

ex3

编写 ex3.sh,该脚本接受一个参数。若改参数不是目录,则给出提示信息;否则使用ll命令列出该目录下的内容,并输出有多少个子目录(d开头),多少个普通文件(-开头)。

05-Shell - 图14

ex4

编写 ex4.sh,将第一个参数指定的内容 copy 到第二个参数指定地点。

  • 若第一个参数是目录,自动添加-r选项(即把目录下的所有内容都 copy 过去);
  • 若第一个参数是普通文件,则将其 copy 到指定地点;
  • 若第一个参数指定的文件或目录不存在,则报错;
  • 若第二个参数指定的文件或目录已经存在,则提示是否替换,若选择 yes,则先删除原来的文件或目录,然后再执行 copy 操作,否则放弃。

05-Shell - 图15

05-Shell - 图16

ex5

编写 ex5.sh。检查命令行的第一个参数是否是-b或者-s

  • 如果是-b,则计算由第二个参数指定的文件中以 b 开头的行数。
  • 如果是-s,则计算由第二个参数指定的文件中以 s 开头的行数。否则显示选择有错的信息。

05-Shell - 图17

ex6

编写 ex6.sh。该脚本需要输入两个文件的名称,然后由用户选择相应的操作(若两个参数中任何一个不是普通文件,则报错)。

05-Shell - 图18

cat:输出两个文件的内容

05-Shell - 图19

statistic:统计两个文件分别有多少行

05-Shell - 图20

merge:将第 1 个文件的内容合并到第 2 个文件后面

05-Shell - 图21

copy:将第 1 个文件的内容 copy 到第 2 个文件(覆盖原文件)

05-Shell - 图22

bye:退出

05-Shell - 图23

1ex

编写 1ex.sh,利用 for 循环将当前目录下的.c 文件移动到指定的目录下,完成后显示指定目录下的文件内容,并按文件从小到大排序。(ll -r -S)

05-Shell - 图24

2ex

编写 2ex.sh,显示 Fibonacci 数列的前 10 项及其总和。

05-Shell - 图25

3ex

编写 ex3.sh,判断给定的参数是否是素数。

05-Shell - 图26

4ex

编写 ex4.sh,将给定的参数转换成二进制表示。

05-Shell - 图27

5ex

ex5.sh假设存在一个/homework的文件夹,其中包含一个 studentlist.csv的文件,当中存放了若干学生的学号,每个一行。例如:150341101、150341102、150341105、150341106,编写 ex11.sh。查看/homework 文件夹下学生是否提交了作业,假设作业名的格式为:学号_homework.txt。最后输出没提交作业的学号名单。

05-Shell - 图28