# 简单的 Shell Script 练习

本章 范例中,实现的方式很多,建议先自行编写,再参考例子,才能加深概念

# 简单范例

本小节范例在很多脚本程序中都会用到,而且简单

# 对谈式脚本:变量类容由用户决定

在很多场景中,需要用户输入一些内容,让程序可以顺利运行。比如,安装软件时,让用户选择安装目录;

BASH 中的变量读取指令 read ,那么以 read 指令的用途实现:

  1. 用户输入 first name
  2. 用户输入 last name
  3. 最后在屏幕上显示:You full name is:xxx
[mrcode@study bin]$ vim read.sh
#!/bin/bash
read -p 'first name: ' firstName
read -p 'last name: ' lastName
echo "You full name: ${firstName}${lastName}"
exit 0
1
2
3
4
5
6
# 增加执行权限
[mrcode@study bin]$ chmod a+x read.sh
# 执行
[mrcode@study bin]$ ./read.sh 
first name: zhu
last name: mrcode
You full name: zhumrcode
1
2
3
4
5
6
7

下面是书上的程序

vim showname.sh
#!/bin/bash
# Program:
#	用户输入姓名,程序显示出输入的姓名
# History:
#	2020/01/19	mrcode	first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input you first name: " firstname		# 提示使用者输入
read -p "Please input you last name: " lastname			# 提示使用者输入
# -e 开启反斜杠转移的特殊字符显示,比如下面的 \n 换行显示
echo -e “\n Your full name is: ${firstname}${lastname}# 结果由屏幕输出	
1
2
3
4
5
6
7
8
9
10
11
12
13
# 执行结果
[mrcode@study bin]$ ./showname.sh 
Please input you first name: zhu
Please input you last name: mrcode

 Your full name is: zhumrcode
1
2
3
4
5
6

笔者小结:可以看到上面这个脚本,增加了一个良好的习惯,就是脚本说明等信息

# 随日期变化:利用 date 进行文件的建立

考虑一个场景,每天备份 MySql 的数据文件,备份文件名以当天日期命名,如 backup.2020-01-19.data.

重点是 2020-01-19 是怎么来的?范例需求如下:

  1. 用户输入一个文件名前缀
  2. 创建出以日期为名的三个空文件(通过 touch 指令),生成 前天、昨天、今天 日期,及格式为:filename_2020-01-19
vim create_3_filename.sh
#!/bin/bash
# Program: 
#       用户输入文件名前缀,生成前天、昨天、今天的三个空文件
# History:
#       2020/01/19      mrcode          first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

echo -e "将使用 ‘touch’ 命令创建 3 个文件"
read -p "请输入文件名:" fileuser

# 容错,使用变量功能判定与赋值默认值
filename=${fileuser:-"filename"}

# date 命令的使用
date1=$(date --date='2 days ago' +%Y-%m-%d)		# 两天前的日期,并格式化显示
date2=$(date --date='1 days ago' +%Y-%m-%d)	
date3=$(date +%Y-%m-%d)

file1="${filename}_${date1}"
file2="${filename}_${date2}"
file3="${filename}_${date3}"

# 在这里其实可以直接拼接文件名
touch "${file1}"
touch "${file2}"
touch "${file3}"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

这里使用了变量的赋值相关功能,详参考:变量功能

运行测试

[mrcode@study bin]$ ./create_3_filename.sh 
将使用 ‘touch’ 命令创建 3 个文件
请输入文件名:mrcode
[mrcode@study bin]$ ll
总用量 16
-rwxrwxr-x. 1 mrcode mrcode 677 119 14:15 create_3_filename.sh
-rwxrwxr-x. 1 mrcode mrcode 239 119 11:25 hello.sh
-rw-rw-r--. 1 mrcode mrcode   0 119 14:15 mrcode_2020-01-17
-rw-rw-r--. 1 mrcode mrcode   0 119 14:15 mrcode_2020-01-18
-rw-rw-r--. 1 mrcode mrcode   0 119 14:15 mrcode_2020-01-19
# 一次正常输入文件名,一次直接按 enter 按键完成输入,查看是否达到默认赋值等功能
1
2
3
4
5
6
7
8
9
10
11

# 数值运算:简单的加减乘除

在变量功能课程中讲解到,需要使用 declare 来定义变量为正数才能进行计算,此外,也可以利用 $((计算表达式)) 来进行数值运算,可惜的是,bashe shell 预设仅支持整数数据。

范例需求:

  1. 要求用户输入两个变量
  2. 将两个变量相乘后输出到屏幕

下面是笔者自己写的

#!/bin/bash
# Program:
#       用户输入 2 个整数;输出相乘后的结果
# History:
#       2020/01/19      mrcode          first release
read -p '请输入第一个整数:' intUser1
read -p '请输入第二个整数:' intUser2
declare -i int1=${intUser1}
declare -i int2=${intUser2}

echo -e  "\n ${int1} x ${int2} = $((int1*int2))"
1
2
3
4
5
6
7
8
9
10
11

测试输出

[mrcode@study bin]$ ./multiplying.sh 
请输入第一个整数:2
请输入第二个整数:3

 2 x 3 = 6
1
2
3
4
5

其实用下面这样的方式来定义和输出

read -p '请输入第一个整数:' intUser1
read -p '请输入第二个整数:' intUser2
total=$((${intUser1}*${intUser2}))		# 使用 $((运算内容)) 方式计算
# declare -i total=${intUser1}*${intUser2}		# 还可以使用此种方式
echo -e  "\n ${intUser1} x ${intUser2} = ${total}"
1
2
3
4
5

建议用 var = $((计算内容)) 方式来计算,此种方式简单。比如

# 取余数
[mrcode@study bin]$ echo $((3 % 2))
1

# 对于小数,可以使用 bc 指令来协助
[mrcode@study bin]$ echo $((3 / 2)); echo "3/2" | bc -l
1
1.50000000000000000000
1
2
3
4
5
6
7
8

# 数值运算:通过 bc 计算 pi

bc 提供了一个计算 pi 的公式: pi=$(echo "scale=10; 4*a(1)" | bc -l),此计算公式可以通过 man bc | grep 'pi' 定位到相关文档。这里的 scale 是计算 pi 的精度,越高则利用到的 cpu 资源越多,计算时间越长。

好了,了解到怎么启用 pi 计算,这里要求用户输入 scale 进行计算 pi 值,并输出显示

vim cal_pi.sh
#!/bin/bash
# Program:
#	用户输入 scale 的值,程序计算出 scale 精度的 pi 值,并显示
# History:
#	2020/01/19		mrcode		first release
# PATH 常规赋值
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p '请输入 scale 的值(10~10000)?' checking
num=${checking:-"10"}

echo -e '\n开始计算 pi 的值'
time echo "scale=${num}; 4*a(1)" | bc -l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

测试输出

[mrcode@study bin]$ ./cal_pi.sh 
请输入 scale 的值(10~10000)?20

开始计算 pi 的值
3.14159265358979323844

real	0m0.002s
user	0m0.000s
sys	0m0.001s
1
2
3
4
5
6
7
8
9

# script 的执行方式差异(source、sh script、./script)

不同的方式执行执行会造成不一样的结果,尤其影响 bash 的环境很大。

# 利用直接执行的方式来执行 script:在子程序中执行

直接指令下达 或者是利用 bash(sh)来运行脚本时,都会使用一个新的 bash 环境来执行脚本的指令。也就是说这种方式执行是在子程序的 bash 内执行的。在第十章 BASH 内谈到 export 自定义变量转成环境变量 的功能时,重点在于:当子程序完成后,子程序内的各项变量或动作将会结束儿不会传回到父程序中。

# 运行上面范例的姓名打印
[mrcode@study bin]$ ./showname.sh 
Please input you first name: m
Please input you last name: q

 Your full name is: mq		#  echo -e "\n Your full name is: ${firstname}${lastname}" 打印出来了信息
[mrcode@study bin]$ echo ${fristname}${lastname}		# 但是在父程序中却没有信息

1
2
3
4
5
6
7
8

# 利用 source 来执行脚本:在父程序中执行

同样的测试代码,使用 source 就不一样了

[mrcode@study bin]$ source showname.sh 
Please input you first name: m
Please input you last name: q

 Your full name is: mq
[mrcode@study bin]$ echo ${firstname}${lastname}
mq			# 在父程序中还能获取到
1
2
3
4
5
6
7