Fork me on GitHub

shell脚本2-函数

函数的定义和使用

  • Linux Shell 中的函数和大多数编程语言中的函数都是一样的
  • 相同功能封装在函数里面方便调用

语法格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name()
{
command1
command2
......
commandn
}
function name
{
command1
command2
......
commandn
}

  • 调用函数直接使用函数名调用就可以了,可以将其想成 Shell 中的一条命令。

  • 函数内部可以直接使用参数 $1,$2…$n

1
2
3
4
test()
{
echo "test function"
}

想调用函数直接在终端输入 test 就可以了。

ex:写一个监控 nginx 的脚本,如果Nginx 服务宕掉了,再把它拉起来。

这里有一个小技巧就是在脚本里面写命令的时候,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
#
# $$能够获取到脚本在运行过程中所产生的pid
this_pid=$$
# 作为一个守护进程,写成一个死循环
while true
do
# 这里要记得在脚本运行的时候将运行产生的pid也要同时过滤掉
ps -ef | grep nginx | grep -v grep | grep -v $this_pid &> /dev/null
# 如果上面的命令能够正确运行,则$?的值为0
if [ $? -eq 0 ];then
# 如果 nginx 脚本
echo "Nginx is running well"
sleep 3
else
systemctl start nginx
echo "Nginx is down,Start it..."
fi
done

这样我们使用sh protectNginx.sh 这个脚本之后,他就会一直执行起来来监视系统里面 nginx 进程的情况。

如果我们不想在终端输出的话,可以使用 nohub 命令

1
2
3
4
# 使用nohub在后台进行运行
nohub sh protectNginx.sh &
# 使用tail -f 查看 nohub 的输出日志
tail -f nohub.out

函数的返回值

返回:return 或者是 echo

return 只能返回 1-255 的整数,一般情况下我们使用 return 状态而已(0表示成功,1表示失败)

echo 可以返回任何字符串的结果,通常用于返回数据,比如一个字符串或者列表值.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/sh
#
# 写一个函数判断nginx进程是否存在
# 先获取到pid
this_pid=$$
# 主题的功能就是判断nginx 的进程是否存在
function is_nginx_running
{
ps -ef | grep nginx | grep -v grep | grep -v $this_pid &> /dev/null
if [ $? -eq 0 ];then
return
else
return 1
fi
}
# 一旦函数执行的结果输出为1就证明nginx的进程是存在的
is_nginx_running && echo "Nginx is Running" || echo "Nginx is stoped!"

我们可以使用sh nginx.sh 来启动脚本,如果 nginx 进程是存在的,$?的值会为1,否则$?的值为0。

我们可以使用sh -x nginx.sh来查看脚本运行过程中值的变化过程。

可以看出来在上面过程中函数的值仅仅是用来做一个判断使用的。

接下面我们使用 return 来构建一个函数:

linux里面我们可以使用cat /etc/passwd 来查看linux下面的用户的信息。
但是里面的信息需要我们自己来清洗一波:

于是我们使用这个命令:cat /etc/passwd | cut -d: -f1

-d: 表示的意思是取输出信息里面的冒号分隔符作为一个数组,类似于JavaScript 里面的split,然后使用f1命令来选取第一个元素。

这样我们就得到了用户的信息,然后我们可以使用遍历的方法来操作一波:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh
#
function get_users
{
users=`cat /etc/passwd | cut -d: -f1`
echo $users
}
userList=`get_users`
for u in $userList
do
echo $u
done

局部变量和全局变量

  • 不做特殊声明,Shell中的变量都是全局变量
  • Tips:大型脚本程序不使用全局变量

局部变量

  • 定义变量的时候,使用 local 关键字
  • 函数内部和外部同时存在同名变量,内部会覆盖掉外部的变量
1
2
3
4
5
6
7
8
9
var1="Hello world"
function test
{
var2=87
}
test
echo $var1
echo $var2

如果我们不调用test的话输出var2就没有值,如果调用一下之后,那么var2就成了一个全局变量的值。

如果我们使用local关键字去命令var2的话,那么就不会输出了.

1
2
3
4
5
6
7
8
9
var1="Hello world"
function test
{
var2=87
}
test
echo $var1
echo $var2

函数库

为什么要定义函数库?

  • 经常使用的重复代码封装成函数文件
  • 一般不直接执行,而是由其他脚本调用

作业:
定义一个函数库,该函数库实现以下几个函数:

  1. 加法函数 add
  2. 减法函数 reduce
  3. 乘法函数 multiple
  4. 除法函数 divide
  5. 打印系统运行情况的函数sys_load,该函数可以显示内存运行和磁盘的使用情况

我们新建一个文件夹

1
2
3
4
mkdir lib
cd lib
touch base_function.sh
touch calcute.sh

base_function.sh 里面封装一堆函数代码:

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
function add
{
# 直接使用函数给的两个参数来做一个命令替换
echo "`expr $1 + $2`"
}
function reduce
{
echo "`expr $1 - $2`"
}
function multiple
{
echo "`expr $1 \* $2`"
}
function divide
{
echo "`expr $1 \/ $2`"
}
function sys_load
{
echo "Memory Info"
echo
free -m
echo
echo "Disk Usage"
echo
df -h
echo
}

然后在calcute.sh 里面调用库函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
#
# 使用`.`将库文件引用进来
# . base_function
# 这里不能使用相对路径,直接使用绝对路径即可
path=`pwd`
. $path/base_function.sh
add 12 23
reduce 90 30
multiple 10 20
divide 30 10
sys_load

这样就差不多完成了.
这里要注意一些这样的问题:

  • 库文件的后缀名是任意的,但是一般是使用.lib结尾
  • 库文件通常没有可执行的选项.(-x 的权限是不给的)
  • 库文件无需和脚本在同一级目录,只需要在脚本引用时指定
  • 第一行一般使用#!bin/echo 来输出警告信息,避免用户执行。
-------------本文结束感谢您的阅读-------------