zoukankan      html  css  js  c++  java
  • shell中的函数

    img

    #什么是函数
    	盛放某一功能的容器
    #为什么要用函数
    	没有引入函数前,遇到重复使用某一个功能的地方,只能复制黏贴实现该功能的代码,这会导致:
    	1.减少代码冗余,解决脚本重复使用某一功能,结构不清晰,可读性差
    	2.可扩展性差,如果要修改功能,需要找到该脚本内所有的该功能才能修改
    #怎么调用函数
    	先定义,后调用
    	把代码从磁盘取出来,放到内存(函数调用),在内存申请一个内存空间,把函数体代码放进去,通过函数名调用
    
    #函数就是命令,脚本也是命令
    	命令可以传参,脚本可以传参,函数可以传参
    	函数的执行相当于开了一个子shell,所以可以使用脚本的位置参数
    	使用$$可以看到,脚本和函数属于一个进程
    

    一、函数定义的格式

    function name() {
        statements
        [return value]
    }
    
    #省略空格
    function name(){
        statements
        [return value]
    }
    
    #省略function
    name() {
        statements
        [return value]
    }
    
    #省略小括号
    function name {
        statements
        [return value]
    }
    

    二、函数的执行

    一:不带参数的执行
    1.function和后面的小括号都不带吗,仅仅时'函数名'就可以执行
    2.函数的定义必须在执行之前定义或加载(即先定义后执行)
    3.函数执行时,会和脚本公用变量,也可以为函数设定局部变量及特殊位置参数 #函数变量的作用域
    4.函数中的return和脚本中的exit + break功能类似,与break的功能相同,return用来退出函数,exit是用来退出脚本,break用来退出循环,continel用来退出本次循环
    5.return的返回值('只能是0-255的整型')会给调用函数的程序,而exit的返回值给执行程序的shell #脚本进程。$?可以查看
    	return的返回值使用$?查看
    	return的返回值可以是变量
    	return后面的同级别代码将不会执行
    	一个函数可以写多个return,同级别值执行一个,不同级别配合if使用
    	return没有echo好用
    6.如果函数独立与脚本之外,被脚本加载使用时,需要使用source或者"."来提前加载使用
      例如:
            . /etc/init.d/functions  
            #加载系统函数中的命令或者参数变量,以供后面的程序使用
      	    #脚本内定义函数的话,可以直接调用系统函数
    7.local定义函数内部的局部变量,变量在离开函数后会消失 #函数变量的作用域
    
    二:带参数的执行:
      函数名 参数1 参数2
    1.shell的位置参数$1、$2、..$# 、$*、$?及$@都可以作为函数的参数使用,此时,父脚本的参数临时被隐藏,而$0仍然是父脚本的名称
    2.当函数执行完成时,原来的命令行脚本的参数恢复
    3.'函数的参数变量'是在函数体里面定义的,	#先添加后调用
    
    #测试位置参数的隐藏,函数体中的位置参数表示传入的参数
    #!/bin/bash
    echo $0
    echo $1
    echo $2
    echo $3
    function name(){
            echo $0
            echo $1
            echo $2
            echo $3
    }
    name
    
    #文件返回值测试
    [root@hass-11 script]# /bin/true;echo $?
    0
    [root@hass-11 script]# /bin/false;echo $?
    1
    
    #action格式
    [root@hass-11 script]# action "注释" /bin/true
    注释                                                       [  OK  ]
    [root@hass-11 script]# action "注释" /bin/false
    注释                                                       [FAILED]
    
    #return与exit,函数体没有return,exit查看的返回值默认是最后一串代码执行之后的返回值
    #!/bin/sh
    function name(){
            echo 111
            xxxxxx
            echo 333
    }
    name
    echo $?
    
    

    小插曲

    1.使用cat命令追加多行,如'打印选项菜单'
    cat <<END
        1.FIRST
        2.SECOND
        3.THIRD 
    END
    
    2.给输出的字体加颜色
    echo -e 识别转义字符,这里识别字符的特殊含义,加颜色
    
     #颜色的开始
     RED_COLOR='E[1;31m'
     GREEN_COLOR='E[1;32m'
     YELLOW_COLOR='E[1;33m'
     BLUE_COLOR='E[1;34m'
    
     #颜色的结束
     RES='E[0m'
      
     echo -e ${RED_COLOR} OLD ${RES}  #OLD是红色
     echo -e ${GREEN_COLOR} OLD ${RES} #OLD是绿色
     echo -e ${YELLOW_COLOR} OLD ${RES} #OLD是黄色
     echo -e ${BLUE_COLOR} OLD ${RES} #OLD是蓝色
    

    定义一个函数,计算所有参数之和

    #!/bin/bash
    function usage(){
        if [ $# -lt 2 ];then
            echo "usage: $0 num1 num2 ..."
        fi
    }
    
    function zhengshu(){
        for i in $*
        do
    	((i++))
    	if [ $? -ne 0 ];then
    	    usage
    	fi
        done
    }
    
    function getsum(){
        local sum=0
        for n in $*
        do
             ((sum+=n))
        done
        return $sum
    }
    
    function main(){
        usage $*
        zhengshu $*
        getsum $*
        echo $?
    }
    
    main $*
    

    作用域

    #什么是作用域
    	变量的作用范围
    	也就是定义一个变量,在哪可以访问到
    #为什么要使用作用域
    	区分变量查找的优先级,避免冲突
    #局部作用域
    	函数内部定义
    	使用local关键字声明
    	只能在"该函数内"使用
    	进程级别的,不同进程之间,局部变量不通用
    	
    子函数不是子进程,是同一个进程,属于一个函数
    #!/bin/sh
    unset x
    x=000
    function syy(){
            echo $x
    }
    function name(){
            local x=111
            syy
    }
    function syy2(){
            echo $x
    }
    echo $x
    name
    syy2
    
    #全局作用域
    	在当前进程内声明
    	不使用关键字
    	可以在"当前进程的任意位置"访问到
    	全局变量是进程级别的
    
    当前shell中执行
    . ./test.sh
    #!/bin/sh
    unset x
    x=222
    function name(){
            local x=111
    }
    echo $x
    
    #只要没有被local声明的变量,都是全局变量
    #shell的变量是进程级别的,python的变量是文件级别的
    	进程级别的变量'在不同的进程'是之间'不能访问'的
    	文件级别的变量在不同的进程之间是可以访问的
    	shell的变量可以做成'伪文件级别'的(使用 export 关键字定义,然后放到全局环境变量文件中)
    	
    #环境变量,文件级别的
    变量的进程隔离
    [root@hass-11 ~]# unset x
    [root@hass-11 ~]# x=1
    [root@hass-11 ~]# echo $x
    1
    [root@hass-11 ~]# bash
    [root@hass-11 ~]# echo $x
    
    环境变量的"传子不传爹"
    [root@hass-11 ~]# export x=2
    [root@hass-11 ~]# exit
    [root@hass-11 ~]# echo $x
    1
    传子,使用export变量定义,临时的,所有子进程中都可以使用
    [root@hass-11 ~]# unset x
    [root@hass-11 ~]# export x=2
    [root@hass-11 ~]# bash
    [root@hass-11 ~]# echo $x
    2
    
    #环境变量文件
    /etc/bashrc   		#推荐,永久生效
    /etc/proifile
    /etc/proifile.d/*	#推荐
    ~/.bashrc
    ~/.bash_profile
    
    #登录式shell与非登录式shell
    	登陆式:su - syy
    	非登陆式:su syy		#不会加载所有的环境变量文件,涉及到脚本能否正常运行
    	
    #内存占用与优化
    	占用
    		每次定义变量都要使用内存空间
    	优化
    		减少bash的个数
    		减少变量的定义
    

    实战题目一:

    用shell脚本检查某网站是否存在异常

    方式一(普通):
    #!/bin/bash
     if [ $# -ne 1 ];then
            usage $"usage $0 url"
     fi
    wget --spider -q -o /dev/null --tries=1 -T 5 $1 
    #--spider用于测试,不下载,-q不在命令中显示 -o 输入到后面的文件中 ,--tries=number为尝试次数和-t一样  -T超时时间和--timeout一样 -S显示响应头
    if [ $? -eq 0 ];then
            echo "$1,up"
    else
            echo "$1,down"
    fi
    方式二(函数封装):
    #!/bin/bash
    function Usage(){
            echo $"Usage:$0 url"
            exit 1
    }
    function check_url(){
            wget --spider -q -o /dev/null -t 1 -T 5 $1 
            if [ $? -eq 0 ];then
                    echo "ok"
            else
                    echo "error"
            fi 
    }
    function main(){
            if [ $# -ne 1 ];then
                    Usage
            else
                    check_url $1
            fi
    }
    main $*  #将脚本传入的参数全部都传到主函数中
    

    实战题目二:

    参数传入脚本、检查某网站是否存在异常,以更专业的方式输出

    #!/bin/bash
    . /etc/init.d/functions  #调用(加载)系统函数,因为下面要用action函数
    function Usage(){
            echo $"usage:$0 url"
            exit 1
    }
    function check_url(){
            wget --spider -q -o /dev/null -t 1 -T 5 $1
            if [ $? -eq 0 ];then
                    action "test $1" /bin/true
            else
                    action "test $1" /bin/false
            fi
    }
    function main (){
            if [ $# -ne 1 ];then
                    Usage
            else
                    check_url $1
            fi
    }
    main $*
    
    效果如下:
    [root@mycentos shell]# sh 3.sh www.baidu.com   
    test www.baidu.com                                         [  OK  ]
    [root@mycentos shell]# sh 3.sh www.baidu.c
    test www.baidu.c                                           [FAILED]
    

    实战题目三:

    用shell开发模块化rsync服务启动脚本

    #!/bin/bash
    #chkconfig:2345 21 81
    #description
    #上面2行是将rsync加入开机自启动服务
    
    #调用系统函数
    . /etc/init.d/functions
    
    #输入错误提示
    function Usage(){
            echo "usage: $0 {start|stop|restart}"
            exit 1
    }
    
    #启动服务
    function Start(){
            rsync --daemon  #启动服务
            sleep 2    #启动服务2秒后再做判断
            if [ $(netstat -pantu | grep rsync | wc -l) -ne 0 ];then
                    action "rsyncd is started" /bin/true
            else
            		
                    action "rsyncd is started" /bin/false
            fi
    }
    #停止服务
    function Stop(){
            killall rsync &>/dev/null
            sleep 2
            if [ $(netstat -apntu| grep rsync | wc -l) -eq 0 ];then
                    action "rsyncd is stopped" /bin/true
            else
                    action "rsyncd is stopped" /bin/false
            fi
    }
    
    case "$1" in
     "start")
            Start
            ;;
     "stop")
            Stop
            ;;
     "restart")
            Stop
            sleep 1
            Start
            ;;
      *)
            Usage
    esac
    
    结果如下:
    [root@mycentos init.d]# /etc/init.d/rsyncd start
    rsyncd is started                                          [  OK  ]
    [root@hass-11 script]# yum install -y lsof
    [root@mycentos init.d]# lsof -i:873
    COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    rsync   1478 root    4u  IPv4  13067      0t0  TCP *:rsync (LISTEN)
    rsync   1478 root    5u  IPv6  13068      0t0  TCP *:rsync (LISTEN)
    [root@mycentos init.d]# /etc/init.d/rsyncd stop
    rsyncd is stopped                                          [  OK  ]
    [root@mycentos init.d]# lsof -i:873
    [root@mycentos init.d]# /etc/init.d/rsyncd restart
    rsyncd is stopped                                          [  OK  ]
    rsyncd is started                                          [  OK  ]
    [root@mycentos init.d]# lsof -i:873               
    COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    rsync   2379 root    4u  IPv4  19734      0t0  TCP *:rsync (LISTEN)
    rsync   2379 root    5u  IPv6  19735      0t0  TCP *:rsync (LISTEN)
    
    注:
    1.在安装或者启动时,如果遇到找不到“/etc/rsync.conf”文件时,要用touch建立一个,实际工作中要写入东西,此处为了方便就不写,为了启动rsync服务即可
    2.rsync具体的脱离xinetd启动的方式见"shell基础二"
    3.放在/etc/init.d目录下就可以用service命令启动,启动前需要是脚本有执行权限,否则无法执行,也无法自动补全。
    4.要加入开机自启动,则需要加入脚本中解释器下面2行,然后“chkconfig --add rsyncd”加入自启动 "rsyncd"是/etc/init.d目录下的服务名称
    

    实战四:

    执行shell脚本,打印如下菜单,柑橘选择,给选择的水果加一种颜色。

    1.红色的苹果
    2.绿色的苹果
    3.黄色的苹果
    4.蓝色的苹果

    方法一(普通版)
    #!/bin/sh
    
    #定义好颜色,方便后面使用
    RED_COLOR='E[1;31m'
    GREEN_COLOR='E[1;32m'
    YELLOW_COLOR='E[1;33m'
    BLUE_COLOR='E[1;34m'
    RES='E[0m'
    
    cat <<END
    ====================
            1.红色的苹果
            2.绿色的苹果
            3.黄色的苹果
            4.蓝色的苹果
    ====================
    END
    
    read -p "input a munber you want:" NUM
    case "$NUM" in
      1)
            echo -e ${RED_COLOR}apple${RES}
            ;;
      2)
            echo -e ${GREEN_COLOR}apple${RES}
            ;;
      3)
            echo -e ${YELLOW_COLOR}apple${RES}
            ;;
      4)
            echo -e ${BLUE_COLOR}apple${RES}
            ;;
      *)
            echo "usage:input {1|2|3|4}"
            exit 1
    esac
    方法二(函数封装):
    #!/bin/sh
    
    #定义好颜色,方便后面使用
    RED_COLOR='E[1;31m'
    GREEN_COLOR='E[1;32m'
    YELLOW_COLOR='E[1;33m'
    BLUE_COLOR='E[1;34m'
    
    RES='E[0m'
    
    function Menu(){
    cat <<END
    ====================
            1.红色的苹果
            2.绿色的苹果
            3.黄色的苹果
            4.蓝色的苹果
    ====================
    END
    }
    function Usage(){
    
            echo "$usage:input a number{1|2|3|4}"
            exit 1
    }
    function Choose(){
    read -p "input a munber you want:" NUM
    case "$NUM" in
      1)
            echo -e ${RED_COLOR}apple${RES}
            ;;
      2)
            echo -e ${GREEN_COLOR}apple${RES}
            ;;
      3)
            echo -e ${YELLOW_COLOR}apple${RES}
            ;;
      4)
            echo -e ${BLUE_COLOR}apple${RES}
            ;;
      *)
            Usage
    esac
    }
    function Main(){
            Menu
            Choose
    }
    Main          
    
    #函数体里面可以再次调用函数
    

    效果如图:

    img

    实战五:紧接着上题,请开发一个给指定内容加上指定颜色的脚本

    #!/bin/bash
    RED_COLOR='E[1;31m'
    GREEN_COLOR='E[1;32m'
    YELLOW_COLOR='E[1;33m'
    BLUE_COLOR='E[1;34m'
    RES='E[0m'
    
    function Usage(){
            echo $"usage:$0 txt {red|green|yellow|pink}"
            exit 1
    }
    
    if [ $# -ne 2 ];then
               Usage
    fi 
    
    function Choose(){
            case "$2" in
                    "red")
                            echo -e ${RED_COLOR}$1${RES}
                            ;;
                    "green")
                            echo -e ${GREEN_COLOR}$1${RES}
                            ;;
                    "yellow")
                            echo -e ${YELLOW_COLOR}$1${RES}
                            ;;
                    "blue")
                            echo -e ${BLUE_COLOR}$1${RES}
                            ;;
                    *)
                    	Usage
            esac
    }
    function Main(){
            Choose $1 $2
    }
    Main $*
    注:
    1.将参数二的颜色付给参数一
    2.精确匹配'单词'的三种方式
      1.grep -w 'oldboy' file 
      2.grep "oldboy" file
      3.grep "^oldboy$" file
    以上都是将出现oldboy单词的行显示出来,而不是将包含oldboy的行显示出来
    

    实战五:

    启动Nginx服务的命令:/application/nginx/sbin/nginx
    关闭Nginx服务的命令:/application/nginx/sbin/nginx -s stop

    请开发脚本,以实现Nginx服务启动和关闭功能,具体脚本命令为/etc/init.d/nginxd {start|stop|restart},并通过chkconfig进行开机自启动

    思路:
    1.判断开启或关闭服务(一是检测pid文件是否存在,存在就是开启,不存在就是服务已经关闭),或者使用netstat链接数也可以
    2.start和stop分别构成函数
    3.对函数和命令运行的返回值进行处理
    4.chkconfig实现服务自启动
    代码:
    #!/bin/sh
    #chkconfig:2345 27 83
    #description
    
    source /etc/init.d/functions  #加载系统函数库
    
    #定义文件路径
    PATH="/application/nginx/sbin"
    PID_PATH="/application/nginx/logs/nginx.pid"
    REVEAL=0
    function Usage(){
            echo $"usage:$0 {start|stop|restart}"
            exit 1
    }
    #判断参数的个数
    if [ $# -ne 1 ];then
            Usage
    fi
    #开始函数
    function Start(){
            if [ ! -f $PID_PATH ];then  #若原来服务是关闭的
                    $PATH/nginx  #开启服务
                    REVEAL=$?  
    
                    if [ $REVEAL -eq 0 ];then  #判断是否开启
                            action "nginx is started" /bin/true
    
                    else
                            action "nginx is started" /bin/false
                    fi
    
            else
                    echo "nginx is running"
    
            fi
            return $REVEAL
    }
    function Stop(){#结束函数
            if [ -f $PID_PATH ];then  #若原来服务是启动的
                    $PATH/nginx -s stop  #关闭服务
                    REVEAL=$?
    
                    if [ $REVEAL -eq 0 ];then  #判断是否关闭
                            action "nginx is stopped" /bin/true
                    else
                            action "nginx is stopped" /bin/false
                    fi
                    return $REVEAL
            else
                    echo "nginx is no running"
            fi
            return $REVEAL
    }
    
            case "$1" in
               "start")
                     Start
                    REVEAL=$?
                    ;;
               "stop")
                    Stop
                    REVEAL=$?
                    ;;
               "restart")
                    Stop
                    Start
                    REVEAL=$?
                    ;;
                    *)
                    Usage
            esac
    
     exit $REVEAL
    

    效果如下:

    [root@mycentos init.d]# /etc/init.d/nginxd start
    nginx is running
    [root@mycentos init.d]# /etc/init.d/nginxd stop
    nginx is stopped                                           [  OK  ]
    [root@mycentos init.d]# /etc/init.d/nginxd start
    nginx is started                                           [  OK  ]
    [root@mycentos init.d]# lsof -i:80
    COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    nginx   2928  root    6u  IPv4  23795      0t0  TCP *:http (LISTEN)
    nginx   2929 nginx    6u  IPv4  23795      0t0  TCP *:http (LISTEN)
    [root@mycentos init.d]# /etc/init.d/nginxd restart
    nginx is stopped                                           [  OK  ]
    nginx is started                                           [  OK  ]
    [root@mycentos init.d]# lsof -i:80                
    COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    nginx   2940  root    6u  IPv4  24824      0t0  TCP *:http (LISTEN)
    nginx   2941 nginx    6u  IPv4  24824      0t0  TCP *:http (LISTEN)
    

    彩蛋一枚

    return :
        1.用来退出函数,后面的函数里面的内容不再执行
    exit:
         2.用来退出脚本,后面的内容不再执行
    
    function test(){
      return 1
    }  
    test    #函数执行完成后,$?会得到test中return后面的返回值
    ls -l ./  #这条命令执行成功,$?变成0
        
    function test2(){
      exit 99
    } 
    test2 #函数执行完成后,$?变成99,也就是exit后面的数值
    $?的值会不断的改变,有别于其他语言的函数调用的返回值。
    

    结果:

    #!/bin/bash
    function test(){
            return 8
    }
    echo $?  #0
    test
    echo $? #8
    
    function test2(){
            return 9
    }
    echo $? #0
    test2
    echo $? #9
    
    ls -l ./
    echo $? #0
    exit 10
    
    此shell执行结束后,echo $? 结果是10
    
  • 相关阅读:
    导出表结构语句
    closeChannel: close the connection to remote address[] result: true
    spingboot使用rabbitmq
    服务器很卡问题排查
    docker-compose安装nginx
    Docker方式安装ShowDoc
    "docker build" requires exactly 1 argument
    Intellij IDEA常用快捷键介绍 Intellij IDEA快捷键大全汇总
    IDEA 2018 3.4 激活破解方法
    jpress:v3.2.5的docker-compose安装
  • 原文地址:https://www.cnblogs.com/syy1757528181/p/13603772.html
Copyright © 2011-2022 走看看