函数的高级用法
讲解 Shell 中函数是如何定义和使用的、如何向函数传递参数、函数的返回值,并剖析和其他语言使用函数的区别;之后介绍如何调用函数、函数的作用域、如何进行递归调用以及函数库的概念,所有的讲解都会配合在真实环境演示,以帮助学员彻底理解...
# 函数的定义和使用
# 函数定义
- Linux Shell 中的函数和大多数编程语言中的函数一样。
- 将相似的任务或代码封装到函数中,供其他地方调用。
# 语法格式
| 语法 | |
|---|---|
| 第一种 | name() { command1 command2 ...... commandn } |
| 第二种 | function name { command1 command2 ...... commandn } |
# 调用函数
直接使用函数名调用,可以将其想象成 Shell 中的一条命令。
函数内部可以直接使用参数 $1、$2 ...... $n。
调用函数:function_name $1 $2。
# 练习
定义函数,调用执行后终端输出
Hello, Zhangsan~。#!/usr/bin/env bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH function hello() { echo -e "Hello, Zhangsan~" } hello1
2
3
4
5
6
7
8
9定义函数,调用执行后控制台循环输出
1 - 10。#!/usr/bin/env bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH function print_num() { for ((i = 0; i <= 10; i++)); do # echo -e "This is $i num: $i .\n" printf "This is %2d num: %2d .\n" $i $i done } print_num1
2
3
4
5
6
7
8
9
10
11
12
# 需求描述
写一个监控 nginx 服务的脚本。如果 nginx 服务宕掉,则该脚本可以检测到并将进程启动。
# 思路分析
- 使用
ps -ef | grep nginx | grep -v grep命令查看 nginx 进程,grep -v grep命令排除 grep 进程。如果脚本名中带有 nginx 字段,还需要排除脚本进程。 - 当进程存在,上述命令返回值为 0,则输出
Nginx is running well!。否则重启该进程。
# 脚本编写
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 监控 nginx 服务的脚本。如果 nginx 服务宕掉,则该脚本可以检测到并将进程启动
this_pid=$$
Green_font_prefix="\033[32m" && Red_font_prefix="\033[31m" && Green_background_prefix="\033[42;37m" && Red_background_prefix="\033[41;37m" && Font_color_suffix="\033[0m"
function check_root() {
if [ ${UID} -ne 0 ]; then
echo -e "当前非 ROOT 账号(或没有 ROOT 权限),无法继续操作,请更换 ROOT 账号或使用 su命令获取临时 ROOT 权限" && exit 1
fi
}
function nginx_daemon() {
# 查看进程是否存在,屏蔽结果信息
ps -ef | grep nginx | grep -v grep | grep -v ${this_pid} &>/dev/null
# echo $?
if [ "$?" -ne 0 ]; then
echo -e "[${Red_font_prefix}$(TZ='America/Los_Angeles' date "+%F %T")]${Font_color_suffix} Nginx is down, Start it ..."
sudo systemctl start nginx
else
echo -e "${Green_font_prefix}[$(TZ='America/Los_Angeles' date "+%F %T")]${Font_color_suffix} Nginx is running well ~"
fi
}
check_root
while true; do
nginx_daemon
sleep 3
done
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
29
30
31
32
33
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
29
30
31
32
33
# 执行结果
[ryan@ryan-tencentcloud-2]~$ sudo su -
[root@ryan-tencentcloud-2]~# cd /home/ryan/
[root@ryan-tencentcloud-2]/home/ryan# systemctl stop nginx
[root@ryan-tencentcloud-2]/home/ryan# nohup sh nginx_daemon.sh &
[1] 117505
[root@ryan-tencentcloud-2]/home/ryan# nohup: ignoring input and appending output to 'nohup.out'
[root@ryan-tencentcloud-2]/home/ryan# systemctl stop nginx
[root@ryan-tencentcloud-2]/home/ryan# ps -aux | grep nginx_daemon.sh | grep -v grep
root 117505 0.1 0.0 113288 1484 pts/1 SN 08:03 0:00 sh nginx_daemon.sh
[root@ryan-tencentcloud-2]/home/ryan# kill -9 117505
[root@ryan-tencentcloud-2]/home/ryan#
[1] + killed nohup sh nginx_daemon.sh
[root@ryan-tencentcloud-2]/home/ryan# cat -n nohup.out
1
2 [2020-10-02 05:03:20] Nginx is down, Start it ...
3 [2020-10-02 05:03:24] Nginx is running well ~
4 [2020-10-02 05:03:27] Nginx is running well ~
5 [2020-10-02 05:03:30] Nginx is running well ~
6 [2020-10-02 05:03:33] Nginx is running well ~
7 [2020-10-02 05:03:36] Nginx is running well ~
8 [2020-10-02 05:03:39] Nginx is running well ~
9 [2020-10-02 05:03:43] Nginx is down, Start it ...
10 [2020-10-02 05:03:46] Nginx is running well ~
11 [2020-10-02 05:03:49] Nginx is running well ~
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
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
# 函数中传递参数
- 函数中传递参数和调用脚本传递参数类似,都是使用 $1 $2 $3 $4 $5 $6 $7 这种方式。
# 语法
| 语法 |
|---|
function-name $1 $2 |
# 练习
# 需求描述
编写一个脚本,该脚本可以实现计算器的功能,可以进行 + - x / %数学运算。
例如:sh calculate.sh 30 + 40 | sh calculate.sh 30 - 40。
# 思路分析
- 数学运算使用 bc 或者 expr 处理。
# 脚本编写
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 实现计算器的功能,可以进行 `+ - x / %`数学运算。
# 例如:`sh calculate.sh 30 + 40 | sh calculate.sh 30 - 40`。
Green_font_prefix="\033[32m" && Red_font_prefix="\033[31m" && Green_background_prefix="\033[42;37m" && Red_background_prefix="\033[41;37m" && Font_color_suffix="\033[0m"
calculate() {
case $2 in
+)
echo -e "$1 $2 $3 = $(expr $1 + $3)"
# echo -e "$1 + $3" | bc
;;
-)
echo -e "$1 $2 $3 = $(expr $1 - $3)"
;;
x)
echo -e "$1 $2 $3 = $(expr $1 \* $3)"
;;
/)
echo -e "$1 $2 $3 = $(expr $1 / $3)"
;;
%)
echo -e "$1 $2 $3 = $(expr $1 % $3)"
;;
*)
echo -e "${Red_font_prefix}ERROR!!!${Font_color_suffix} Pls input a operator in (+|-|x|/|%)!"
;;
esac
}
calculate $1 $2 $3
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
29
30
31
32
33
34
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
29
30
31
32
33
34
# 执行结果
[ryan@ryan-tencentcloud-2]~ sh calculate.sh 20 + 3
20 + 3 = 23
[ryan@ryan-tencentcloud-2]~ sh calculate.sh 20 - 3
20 - 3 = 17
[ryan@ryan-tencentcloud-2]~ sh calculate.sh 20 x 3
20 x 3 = 60
[ryan@ryan-tencentcloud-2]~ sh calculate.sh 20 / 3
20 / 3 = 6
[ryan@ryan-tencentcloud-2]~ sh calculate.sh 20 % 3
20 % 3 = 2
[ryan@ryan-tencentcloud-2]~ sh calculate.sh 20 * 30
ERROR!!! Pls input a operator (+|-|x|/|%)!
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 函数返回值
# 语法
| 返回值方式 | |
|---|---|
| 方法一 | return |
| 方法二 | echo |
- 使用 return 返回值
- 只能返回 1 - 255 的整数
- 函数使用 return 返回值,通常只是用来供其它地方调用获取状态。因此,通常仅返回 0 或 1。0 表示成功, 1 表示失败。
- 使用 echo 返回值
- 可以返回任何字符串结果
- 通常用于返回数据,比如一个字符串值或者列表值。
# 练习 1
# 需求描述
判断 nginx 进程是否存在。如果存在终端输出 "Nginx is running!",否则终端输出"Nginx is stoped~"。
# 思路分析
- 使用
ps -ef | grep nginx | grep -v grep命令查看 nginx 进程,grep -v grep命令排除 grep 进程。如果脚本名中带有 nginx 字段,还需要排除脚本进程。 - 函数执行结果为 0 时输出 "Nginx is running!",否则终端输出"Nginx is stoped~"。
# 脚本编写
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 判断 nginx 进程是否存在。
# 如果存在终端输出 "Nginx is running!". 否则终端输出"Nginx is stoped~"
this_pid=$$
is_nginx_running() {
ps -ef | grep nginx | grep -v grep | grep -v ${this_pid} &>/dev/null
if [ $? -eq 0 ]; then
return # return 0
else
return 1
fi
}
is_nginx_running && echo -e "Nginx is running!" || echo -e "Nginx is stoped~"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 代码执行
[ryan@ryan-tencentcloud-2]~ sh -x is_nginx_running.sh #sh -x 查看执行过程
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/ryan/bin
+ export PATH
+ this_pid=119050
+ is_nginx_running
+ ps -ef
+ grep nginx
+ grep -v grep
+ grep -v 119050
+ '[' 0 -eq 0 ']'
+ return
+ echo -e 'Nginx is running!'
Nginx is running!
[ryan@ryan-tencentcloud-2]~ sudo systemctl stop nginx
[ryan@ryan-tencentcloud-2]~ sh -x is_nginx_running.sh
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/ryan/bin
+ export PATH
+ this_pid=119098
+ is_nginx_running
+ grep -v grep
+ grep -v 119098
+ ps -ef
+ grep nginx
+ '[' 1 -eq 0 ']'
+ return 1
+ echo -e 'Nginx is stoped~'
Nginx is stoped~
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
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
# 练习 2
# 需求描述
获取系统所有用户并输出。
# 思路分析
- 系统所有用户在
/etc/passwd文件中。 - 使用
cut -d ":" -f 1命令截取输出结果中的第一列。cut命令默认属于使用TAB作为分隔符,使用-d ":"命令指定:为分隔符。使用-f 1参数指定获取第一列。
# 脚本编写
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 获取系统所有用户并输出
get_user() {
user=$(cat /etc/passwd | cut -d ":" -f 1)
echo ${user}
}
user_list=$(get_user)
index=0
for user in ${user_list}; do
# echo -e "This is ${index} user: ${user} ."
printf "This is %02d user: %s\n" ${index} ${user}
index=$((${index} + 1))
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 代码执行
[ryan@ryan-tencentcloud-2]~ sh get_user.sh
This is 00 user: root
This is 01 user: bin
This is 02 user: daemon
This is 03 user: adm
This is 04 user: lp
This is 05 user: sync
This is 06 user: shutdown
This is 07 user: halt
This is 08 user: mail
This is 09 user: operator
This is 10 user: games
This is 11 user: ftp
This is 12 user: nobody
This is 13 user: systemd-network
This is 14 user: dbus
This is 15 user: polkitd
This is 16 user: libstoragemgmt
This is 17 user: colord
This is 18 user: rpc
This is 19 user: abrt
This is 20 user: rtkit
This is 21 user: pulse
This is 22 user: chrony
This is 23 user: gluster
This is 24 user: ntp
This is 25 user: tss
This is 26 user: sssd
This is 27 user: geoclue
This is 28 user: mysql
This is 29 user: setroubleshoot
This is 30 user: gdm
This is 31 user: rpcuser
This is 32 user: nfsnobody
This is 33 user: sshd
This is 34 user: postfix
This is 35 user: tcpdump
This is 36 user: ryan
This is 37 user: nginx
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
29
30
31
32
33
34
35
36
37
38
39
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
29
30
31
32
33
34
35
36
37
38
39
# 局部变量和全局变量
# 全局变量
- 不做特殊声明,Shell 中变量都是全局变量。
- Tips:大型脚本程序中函数慎用全局变量。
# 局部变量
- 定义变量时,使用 local 关键字定义局部变量。
- 如果函数内和函数外存在同名变量,则函数内部变量会覆盖外部变量。
# 练习
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 全局变量、局部变量练习
var1="Hello World"
test() {
var1="Hello Ryan~"
echo -e "函数内部定义的全部变量 var1 值为: ${var1}"
var2=88
local var3=99
}
echo -e "函数外部定义的全部变量 var1 值为: ${var1}"
test
echo -e "函数内部定义的全部变量 var2 值为: ${var2}"
echo -e "函数内部定义的局部变量 var3 值为: ${var3}"
# Output
#
# 函数外部定义的全部变量 var1 值为: Hello World
# 函数内部定义的全部变量 var1 值为: Hello Ryan~
# 函数内部定义的全部变量 var2 值为: 88
# 函数内部定义的局部变量 var3 值为:
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
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
# 函数库
# 定义
- 经常使用的重复代码封装成函数文件;
- 一般不直接执行,而是由其它脚本调用。
- 库文件名的后缀是任意的,但一般使用
.lib。 - 库文件通常没有可执行选项。
- 库文件无需和脚本在同级目录,只需在脚本中引用时指定详细路径。
- 库文件第一行一般使用
#!/bin/echo输出警告信息,避免用户直接执行。
# 练习
# 需求描述
定义一个函数库,该函数实现以下几个函数:
- 加法函数
add - 减法函数
reduce - 乘法函数
multiple - 除法函数
divide - 打印系统运行情况的函数
sys_load,该函数可以显示内存运行情况、磁盘使用情况。
# 思路分析
- 加减乘除运算使用 expr 进行计算。
- 内存运行情况
free -m,磁盘使用情况df -h。
# 脚本编写
#!/bin/echo
add() {
echo -e "$1 + $2 = $(expr $1 + $2)"
}
reduce() {
echo -e "$1 - $2 = $(expr $1 - $2)"
}
multiple() {
echo -e "$1 * $2 = $(expr $1 \* $2)"
}
divide() {
echo -e "$1 / $2 = $(expr $1 / $2)"
}
sys_load() {
echo -e "-------- Memory Info --------"
free -m
echo -e "-----------------------------\n"
echo -e "------ Disk Usage Info ------"
df -h
echo -e "-----------------------------"
}
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
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
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
. /home/ryan/base_function.lib
add 20 3
reduce 20 3
multiple 20 3
divide 20 3
sys_load
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 代码执行
20 + 3 = 23
20 - 3 = 17
20 * 3 = 60
20 / 3 = 6
-------- Memory Info --------
total used free shared buff/cache available
Mem: 1987 643 208 1 1135 1199
Swap: 1024 290 734
-----------------------------
------ Disk Usage Info ------
Filesystem Size Used Avail Use% Mounted on
devtmpfs 981M 0 981M 0% /dev
tmpfs 994M 24K 994M 1% /dev/shm
tmpfs 994M 732K 993M 1% /run
tmpfs 994M 0 994M 0% /sys/fs/cgroup
/dev/vda1 50G 9.2G 38G 20% /
tmpfs 199M 0 199M 0% /run/user/0
tmpfs 199M 0 199M 0% /run/user/1000
-----------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21