Shell Shell
Home
Shell
Linux
Network
Git
Blog (opens new window)
GitHub (opens new window)
Home
Shell
Linux
Network
Git
Blog (opens new window)
GitHub (opens new window)
  • Shell 脚本编程

    • 目录
    • 变量的高级用法
    • 函数的高级用法
      • 函数的定义和使用
        • 函数定义
        • 语法格式
        • 调用函数
        • 练习
      • 函数中传递参数
        • 语法
        • 练习
      • 函数返回值
        • 语法
        • 练习 1
        • 练习 2
      • 局部变量和全局变量
        • 全局变量
        • 局部变量
        • 练习
      • 函数库
        • 定义
        • 练习
    • 文件查找命令 find
    • 文本处理命令 grep
    • 文本处理命令 sed
  • shell
  • Shell 脚本编程
JaimeZeng
2021-02-21

函数的高级用法

讲解 Shell 中函数是如何定义和使用的、如何向函数传递参数、函数的返回值,并剖析和其他语言使用函数的区别;之后介绍如何调用函数、函数的作用域、如何进行递归调用以及函数库的概念,所有的讲解都会配合在真实环境演示,以帮助学员彻底理解...

# 函数的定义和使用

# 函数定义

  • Linux Shell 中的函数和大多数编程语言中的函数一样。
  • 将相似的任务或代码封装到函数中,供其他地方调用。

# 语法格式

语法
第一种 name() {
command1
command2
......
commandn
}
第二种 function name {
command1
command2
......
commandn
}

# 调用函数

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

  • 函数内部可以直接使用参数 $1、$2 ...... $n。

  • 调用函数:function_name $1 $2。

# 练习

  1. 定义函数,调用执行后终端输出 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~"
    }
    
    hello
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 定义函数,调用执行后控制台循环输出 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_num
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# 需求描述

写一个监控 nginx 服务的脚本。如果 nginx 服务宕掉,则该脚本可以检测到并将进程启动。

# 思路分析

  1. 使用 ps -ef | grep nginx | grep -v grep 命令查看 nginx 进程,grep -v grep 命令排除 grep 进程。如果脚本名中带有 nginx 字段,还需要排除脚本进程。
  2. 当进程存在,上述命令返回值为 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

# 执行结果

[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

# 函数中传递参数

  • 函数中传递参数和调用脚本传递参数类似,都是使用 $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

# 执行结果

[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

# 函数返回值

# 语法

返回值方式
方法一 return
方法二 echo
  • 使用 return 返回值
    • 只能返回 1 - 255 的整数
    • 函数使用 return 返回值,通常只是用来供其它地方调用获取状态。因此,通常仅返回 0 或 1。0 表示成功, 1 表示失败。
  • 使用 echo 返回值
    • 可以返回任何字符串结果
    • 通常用于返回数据,比如一个字符串值或者列表值。

# 练习 1

# 需求描述

判断 nginx 进程是否存在。如果存在终端输出 "Nginx is running!",否则终端输出"Nginx is stoped~"。

# 思路分析

  1. 使用 ps -ef | grep nginx | grep -v grep 命令查看 nginx 进程,grep -v grep 命令排除 grep 进程。如果脚本名中带有 nginx 字段,还需要排除脚本进程。
  2. 函数执行结果为 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

# 代码执行

[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

# 需求描述

获取系统所有用户并输出。

# 思路分析

  1. 系统所有用户在 /etc/passwd 文件中。
  2. 使用 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

# 代码执行

[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

# 局部变量和全局变量

# 全局变量

  • 不做特殊声明,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

# 函数库

# 定义

  • 经常使用的重复代码封装成函数文件;
  • 一般不直接执行,而是由其它脚本调用。
  • 库文件名的后缀是任意的,但一般使用.lib。
  • 库文件通常没有可执行选项。
  • 库文件无需和脚本在同级目录,只需在脚本中引用时指定详细路径。
  • 库文件第一行一般使用 #!/bin/echo 输出警告信息,避免用户直接执行。

# 练习

# 需求描述

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

  1. 加法函数 add
  2. 减法函数 reduce
  3. 乘法函数 multiple
  4. 除法函数 divide
  5. 打印系统运行情况的函数 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
#!/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

# 代码执行

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

本文代码:03-shell (opens new window)

变量的高级用法
文件查找命令 find

← 变量的高级用法 文件查找命令 find→

Theme by Vdoing | Copyright © 2020-2022 JaimeZeng | ❤️ | CC BY-NC-SA 4.0
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式