• shell脚本简略


    概述

    shell脚本不管是开发还是运维,肯定都会遇到的。本文作者从脚本长啥样子来稍微阐述一下,本文对于那些熟悉linux系统命令但是没有写过脚本的人比较合适,内容上面比较简单,基本覆盖了常见的一些脚本的内容,但是具体的脚本功能需要配合不同的软件命令来进行针对性编写。

    脚本

    头部解释器

    一般脚本的第一行都会带上

    需要bash解释器的脚本

    #! /bin/bash
    

    需要python解释器的脚本

    #! /bin/python
    或者
    #! /usr/bin/python
    

    此外还有其他的一些类型,可以从/etc/shells里查到其他的解释器。其中最常用的几种是: Bourne shell (sh)、C shell (csh) 和 Korn shell (ksh), 各有优缺点。Bourne shell 是 UNIX 最初使用的 shell,并且在每种 UNIX 上都可以使用, 在 shell 编程方面相当优秀,但在处理与用户的交互方面做得不如其他几种shell。Linux 操作系统缺省的 shell 是Bourne Again shell,它是 Bourne shell 的扩展,简称 Bash,与 Bourne shell 完全向后兼容,并且在Bourne shell 的基础上增加、增强了很多特性。Bash放在/bin/bash中,它有许多特色,可以提供如命令补全、命令编辑和命令历史表等功能,它还包含了很多 C shell 和 Korn shell 中的优点,有灵活和强大的编程接口,同时又有很友好的用户界面。

    /bin/sh
    /sbin/nologin
    /bin/dash
    /bin/tcsh
    /bin/csh
    
    

    应该说, /bin/sh 与 /bin/bash 虽然大体上没什么区别, 但仍存在不同的标准. 标记为 “#!/bin/sh” 的脚本不应使用任何 POSIX 没有规定的特性 (如 let 等命令, 但 “#!/bin/bash” 可以). Debian 曾经采用 /bin/bash 更改 /bin/dash,目的使用更少的磁盘空间、提供较少的功能、获取更快的速度。但是后来经过 shell 脚本测试存在运行问题。因为原先在 bash shell 下可以运行的 shell script (shell 脚本),在 /bin/sh 下还是会出现一些意想不到的问题,不是100%的兼用。

    脚本信息注释

    这部分是脚本的作用和编写时间,包含编写的人,修改的内容等等,没有固定的格式,但是建议这部分作为脚本的,可以参考以下格式

    #--------------------------------------------
    # name:shell.sh
    # author:xxxx
    # date:2019-06-01
    # description:xxxxxxx
    # parameter:$0:xxx $1:xxx $2:xxx
    #--------------------------------------------
    ##### 用户配置区 开始 #####
    #
    #
    # 这里可以添加脚本描述信息
    # 
    #
    ##### 用户配置区 结束  #####
    --------------------- 
    

    脚本参数

    脚本参数是指用户运行脚本后面带的参数,为了让脚本的灵活性更强,通常会在脚本后面加入参数传递给脚本进行执行。但是建议脚本后面的参数不要超过三个。脚本后面的参数依此是$1 $2 $3等等,$0是脚本本身的字符串名。但是注意为了让脚本出错概率较小,在脚本中对后面的参数个数进行检查,如限制一个参数。$#输出的是脚本后面所有的参数个数。

    $n :表示第几个参数,$1 表示第一个参数,$2 表示第二个参数,依此类推   $0:当前程序的名称
    $# :传递给程序的总的参数数目  
    $? :上一个代码或者shell程序在shell中退出的情况,如果正常退出则返回0,反之为非0值。   
    $* :传递给程序的所有参数组成的字符串。   
    $@ :以"参数1" "参数2" ... 形式保存所有参数   
    $$ :本程序的(进程ID号)PID   
    $! :上一个命令的PID
    

    例子:

    if [ $# -ne 1 ];then
            echo -e "Usage: ./vacuumgp.sh  < dbname > 
     "
            echo -e "Example : ./vacuumgp.sh postgres"
            exit 8
    fi
    

    变量申明

    基本变量

    基本变量有字符串和数字。shell是一个弱类型的,赋值不需要进行类型声明。

    my_path="/usr/local"
    num=10
    

    脚本路径

    为了体现脚本的良好移植性,不管脚本在什么目录下,都能够正确执行不报错,那么文件的路径就很重要,因为一般来说,脚本同时会输出日志等信息,可能还会调用其他同目录的脚本,就需要得到脚本的一个当前目录的变量,用于程序主体调用。

    mypath=$(cd `dirname $0`;pwd)
    cd "${mypath}"
    

    时间

    获取今天的日期

    `date '+%Y%m%d %H:%M:%S'` 或 `date +%F` 或 $(date '+%Y%m%d %H:%M:%S')
    

    定义文件名

    filename="`date +%y%m%d`_etc.tar.gz" 
    

    详细的时间域如下:

    格式 详细
    %H 小时(00..23)
    %I 小时(01..12)
    %k 小时(0..23)
    %l 小时(1..12)
    %M 分(00..59)
    %p 显示出AM或PM
    %r 时间(hh:mm:ss AM或PM),12小时
    %s 从1970年1月1日00:00:00到目前经历的秒数
    %S 秒(00..59)
    %T 时间(24小时制)(hh:mm:ss)
    %X 显示时间的格式(%H:%M:%S)
    %Z 时区 日期域
    %a 星期几的简称( Sun..Sat)
    %A 星期几的全称( Sunday..Saturday)
    %b 月的简称(Jan..Dec)
    %B 月的全称(January..December)
    %c 日期和时间( Mon Nov 8 14:12:46 CST 1999)
    %d 一个月的第几天(01..31)
    %D 日期(mm/dd/yy)
    %h 和%b选项相同
    %j 一年的第几天(001..366)
    %m 月(01..12)
    %w 一个星期的第几天(0代表星期天)
    %W 一年的第几个星期(00..53,星期一为第一天)
    %x 显示日期的格式(mm/dd/yy)
    %y 年的最后两个数字( 1999则是99)
    %Y 年(例如:1970,1996等)

    数组

    bash shell只支持一维的数组,使用()来定义,元素之间用空格分开。如

    # 定义一个数组
    table_list=("table1" "table2" "table3")
    list=(1 2 3 4 5)
    
    #获取数组带下标即可
    table_list[0]
    table_list[2]
    #打印变量需要使用花括号
    echo ${table_list[0]}
    echo ${table_list[2]}
    
    #获取所有的变量
    table_list[*]
    #或者
    table_list[@]
    
    #打印所有变量,本身是一个迭代器
    echo ${table_list[*]}
    
    # 打印数组长度
    echo ${#table_list[@]}
    echo ${#table_list[*]}
    
    

    脚本主体

    单引号、倒引号和双引号的区别

    脚本主体中包含了很多的内容,除了逻辑结构,还有大量的命令结构,比如将脚本中执行linux命令结果字符串传入到变量,在脚本中输出格式化字符串等等。最常用的就是下面的三个了。

    ` :如果被倒引号括起来,表示里面执行的是命令。

    “” :如果被双引号括起来,里面出现$(表示取变量名),`表示执行命令,表示转义,其余的才表示字符串。

    '' :如果被单引号括起来,里面表示的全部是字符串。

    测试语句

    测试语句在实际中使用也非常多,要注意条件测试部分中的空格。在方括号的两侧都有空格,在-f、-lt、=等符号两侧同样也有空格。如果没有这些空格,Shell解释脚本的时候就会出错。

    # 
    [ -f "$file" ] : 判断$file是否是一个文件
    [ -e "$file" ] : 判断$file是否存在
    [ -x "$file" ] : 判断$file是否存在且有可执行权限,同样-r测试文件可读性
    [ -n "$a" ] : 判断变量$a是否有值
    [ -z "$a" ] : 判断变量$a是否为空字符串
    [ $a -lt 3 ] : 判断$a的值是否小于3,同样-gt和-le分别表示大于或小于等于
    [ "$a" = "$b" ] : 判断$a和$b的取值是否相等
    [ cond1 -a cond2 ] : 判断cond1和cond2是否同时成立,-o表示cond1和cond2有一成立
    

    实际一个例子,判断当前文件夹是否存在配置文件kong.conf,不存在的话就创建

    if [ ! -e "kong.conf" ];
      then touch kong.conf
    fi
    

    循环语句

    关于循环,日常作者使用比较多的就是遍历数组中的元素,对数组中的元素逐个进行操作。

    • while循环格式
    while [ cond1 ] && { || } [ cond2 ] …; do
        …
    done
    

    下面列举一个小例子,将hosts文件循环读出来。

    while read line; do
        echo $line;
    done < /etc/hosts;
    
    #另外一个例子,方便理解
    i=10;
    while [[ $i -gt 5 ]]; do
        echo $i;
        ((i--));
    done;
    
    • for循环格式
    for var in …; do
        …
    done
    #或者
    for (( cond1; cond2; cond3 )) do
        …
    done
    

    下面仅列出一个小例子。

    tables=("table1" "table2" "table3")
    database="mydb"
    for table in ${tables[*]}
    do 
    vacuumdb --analyze --table $table $database 
    echo "table $table has finished vacuum.">>/tmp/pg_vacuum.log 
    done 
    
    • until循环
    until [ cond1 ] && { || } [ cond2 ] …; do
        …
    done
    

    下面给一个小例子:

    a=10;
    until [[ $a -lt 0 ]]; do
        echo $a;
        ((a--));
    done;
    

    条件语句

    if条件语句在进行逻辑判断时非常有用。

    格式:

    if …; then
        …
    elif …; then
        …
    else
        …
    fi
    

    实际例子:这个例子功能是检查目标文件夹是否存在输入日期的文件夹,如果存在则往下执行,如果不存在则创建。

    read -p "Please input the Date(eg.20180717):" data_date
    if [ ! -d /data1/remote/$IP/${data_date:0:4}/${data_date:0:6} ];
    	then
    	echo "The target folder is not exist,creating..."
    	mkdir -p /data1/remote/$IP/${data_date:0:4}/${data_date:0:6}
    	echo "Target folder is created!"
    fi
    
    

    分支语句

    case/esac语句,这个在实际中出现的场景是脚本和用户进行简单的交互,例如序号选择,yes/no等。可以在linux系统中使用help case来查看用法。

    case var in
        pattern 1 )
            … ;;
        pattern 2 )
            … ;;
        *)
            … ;;
    esac
    
    

    例子,由脚本的第一个参数来判断执行哪一部分的脚本

    case $1 in
        start | begin)
            echo "start something"  
        ;;
        stop | end)
            echo "stop something"  
        ;;
        *)
            echo "Ignorant"  
        ;;
    esac
    

    有一个更为常用的select语句配合使用,用户可以从一组不同的值中进行选择。

    格式:

    select var in …; do
        break;
    done
    

    例子,这里需要注意的是,用户输入的是1,2或3

    select ch in "postgresql" "mysql" "exit"; do
        case $ch in
            "postgresql")
                echo "start install postgresql"  
            ;;
            "mysql")
                echo "start install mysql"  
            ;;
            "exit")
                echo "exit"  
                break;
            ;;
            *)
                echo "error,please select "1~3""  
            ;;
        esac
    done;
    

    函数

    函数的一个好处就是可以重复调用,但是一般来说,代码中完成的功能比较简单和单一,但是对于一些较大的脚本来说,函数是必不可少的。

    下面举一个非常贱的函数例子,功能是计算所有参数的和。

    #!/bin/bash
    
    function getsum(){
        local sum=0
    
        for n in $@
        do
             ((sum+=n))
        done
    
        return $sum
    }
    
    getsum 10 20 55 15  #调用函数并传递参数
    echo $?
    

    注:$@表示函数的所有参数,$?表示函数的退出状态(返回值)

    其他

    计划任务cron

    一般来说,脚本是为了完成某个任务而制定的,但是很多情况下,脚本是需要进行定时执行的,因此,需要学会怎么使用crontab命令了。

    crontab -l #查看当前用户的配置了哪些计划任务
    crontab -e #进入编辑模式
    

    介绍一下格式

    *            *              *           *           *            command
    分钟        小时           号           月         星期           命令
    (0~59)      (0~23)        (1-31)       (1~12)      (0~6)          脚本
    

    例子:

    0 * * * * /home/gpadmin/test.sh #每小时0分执行一次
    0,30 * * * *  /home/gpadmin/test.sh #每小时0分和30分各执行一次
    * * * * * command # 每分钟执行一次
    3,15 8-11 * * * command # 在上午8点到11点的第3和第15分钟执行
    3,15 8-11 */2 * * command #每隔两天的上午8点到11点的第3和第15分钟执行
    3,15 8-11 * * 1 command # 每个星期一的上午8点到11点的第3和第15分钟执行
    30 21 * * * /etc/init.d/smb restart # 每晚的21:30重启smb 
    45 4 1,10,22 * * /etc/init.d/smb restart # 每月1、10、22日的4 : 45重启smb 
    10 1 * * 6,0 /etc/init.d/smb restart # 每周六、周日的1 : 10重启smb
    0,30 18-23 * * * /etc/init.d/smb restart # 每天18 : 00至23 : 00之间每隔30分钟重启smb 
    0 23 * * 6 /etc/init.d/smb restart # 每星期六的晚上11 : 00 pm重启smb
    

    后记

    本文只是涉及到很少的内容,但是基本上是常用的。如果要把所有的shell编程都写下来,都可以成一本书了,随笔的好处就是把自己想到的都写下来,希望对自己或者其他人有帮助,而且是日积月累的过程。如果需要学习,可以看一些开源软件的脚本是如何编写的,学习最好来源于实践。比如mysql的启动脚本,tomcat的启动脚本等。

  • 相关阅读:
    jQuery中ajax的4种常用请求方式
    前端常见浏览器兼容性问题解决方案
    平衡二叉树---将有序数组转换为二叉搜索树
    equals
    Java中的System.out.println
    System.arraycopy --全面解释(就是那么有爱心)
    计算机网络实验-ACL-OSPF-RIP
    pip install lxml
    Spark-shell --常用命令 (command)
    《Thinking in Java》---Summary of Chapter 2
  • 原文地址:https://www.cnblogs.com/easonbook/p/10951632.html
Copyright © 2020-2023  润新知