美文网首页马哥Linux
8-shell脚本基础

8-shell脚本基础

作者: Liang_JC | 来源:发表于2020-02-28 11:49 被阅读0次

    本章内容

    ◆ 编程基础
    ◆ 脚本基本格式
    ◆ 变量
    ◆ 运算
    ◆ 条件测试
    ◆ 条件判断if
    ◆ 条件判断case
    ◆ 配置用户环境

    编程基础

    ● Linus:Talk is cheap, show me the code  
    ● 程序  
        程序:算法+数据结构  
        数据:是程序的核心  
        数据结构:数据在计算机中的类型和组织方式  
        算法:处理数据的方式  
    ● 程序编程风格:  
        过程式:以指令为中心,数据服务于指令  
        对象式:以数据为中心,指令服务于数据  
    ● shell程序:提供了编程能力,解释执行  
    

    程序的执行方式

    ● 计算机:运行二进制指令  
    ● 编程语言:人与计算机之间交互的语言  
    ● 低级编程语言:  
        机器:二进制的0和1的序列,称为机器指令。与自然语言差异太大,难懂、难写  
        汇编:用一些助记符号替代机器指令,称为汇编语言  
            如:ADD A,B 将寄存器A的数与寄存器B的数相加得到的数放到寄存器A中  
            汇编语言写好的程序需要汇编程序转换成机器指令  
            汇编语言稍微好理解,即机器指令对应的助记符,助记符更接近自然语言  
    ● 高级编程语言:  
        编译:高级语言-->编译器-->机器代码-->执行  
            C,C++  
        解释:高级语言-->执行-->解释器-->机器代码  
            shell,python,php,JavaScript,perl  
    

    编程基本概念

    ● 编程逻辑处理方式:  
        顺序执行  
        循环执行  
        选择执行  
    ● shell编程:过程式、解释执行  
        编程语言的基本结构:  
            各种系统命令的组合  
            数据存储:变量、数组  
            表达式:a + b  
            语句:if  
    

    shell脚本基础

    ● shell脚本:  
        包含一些命令或声明,并符合一定格式的文本文件  
    ● 格式要求:首行shebang机制  
        #!/bin/bash  
        #!/usr/bin/python  
        #!/usr/bin/perl  
    ● shell脚本的用途有:  
        自动化常用命令  
        执行系统管理和故障排除  
        创建简单的应用程序  
        处理文本或文件  
    

    创建shell脚本

    ● 第一步:使用文本编辑器来创建文本文件  
        第一行必须包括shell声明序列:#!  
            示例:#!/bin/bash  
        添加注释  
            注释以#开头  
    ● 第二步:运行脚本  
        给予执行权限,在命令行上指定脚本的绝对或相对路径  
        直接运行解释器,将脚本作为解释器程序的参数运行  
    

    脚本规范

    ● 脚本代码开头约定  
        1、第一行一般为调用使用的语言  
        2、程序名,避免更改文件名为无法找到正确的文件  
        3、版本号  
        4、更改后的时间  
        5、作者相关信息  
        6、该程序的作用,及注意事项  
        7、最后是各版本的更新简要说明  
    

    脚本的基本结构

    ● 脚本的基本结构  
        #!SHEBANG  
        CONFIGURATION_VARIABLES  
        FUNCTION_DEFINITIONS  
        MAIN_CODE  
    

    #家目录下添加.vimrc文件

    set tabstop=4       #table键4个空格  
    set ignorecase      #忽略大小写  
    set cursorline      #光标带下划线  
    set autoindent      #自动对齐  
    autocmd BufNewFile *.sh exec ":call SetTitle()"  
    func SetTitle()  
        if expand("%:e") == 'sh'  
        call setline(1,"#!/bin/bash")   
        call setline(2,"#")   
        call setline(3,"#********************************************************************")   
        call setline(4,"#Author:        LiangJC")   
        call setline(5,"#QQ:            184116857")   
        call setline(6,"#Date:          ".strftime("%Y-%m-%d"))  
        call setline(7,"#FileName:      ".expand("%"))  
        call setline(8,"#URL:           http://www.magedu.com")  
        call setline(9,"#Description:       The test script")   
        call setline(10,"#Copyright (C):    ".strftime("%Y")." All rights reserved")  
        call setline(11,"#********************************************************************")   
        call setline(12,"")   
        endif  
    endfunc  
    autocmd BufNewFile * normal G  
    

    shell脚本示例

    #!/bin/bash  
    # ------------------------------------------  
    # Filename: hello.sh  
    # Revision: 1.1  
    # Date: 2017/06/01  
    # Author: wang  
    # Email: wang@gmail.com  
    # Website: www.magedu.com  
    # Description: This is the first script  
    # Copyright: 2017 wang  
    # License: GPL  
    # ------------------------------------------  
    echo "hello world"  
    

    脚本调试

    ● 检测脚本中的语法错误  
        bash -n /path/to/some_script  
    ● 调试执行  
        bash -x /path/to/some_script  
    

    变量

    ● 变量:命名的内存空间  
    ● 变量:变量类型  
        作用:  
            1、数据存储方式  
            2、参与的运算  
            3、表示的数据范围  
        类型:  
            字符  
            数值:整型、浮点型  
    
    ● 静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如:java,c  
    ● 动态编译语言:不用事先声明,可随时改变类型,如bash,Python  
    ● 强类型语言:不同类型数据操作,必须经过强制转换才同一类型才能运算,如java , c# ,python  
        如:以下python代码  
        print(‘magedu’+ 10) 提示出错,不会自动转换类型  
        print(‘magedu’+str(10)) 结果为magedu10,需要显示转换类型  
    ● 弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用  
        如:bash 不支持浮点数,php,javascript  
        
    ● Shell中变量命名法则:  
        1、不能使程序中的保留字:例如if, for  
        2、只能使用数字、字母及下划线,且不能以数字开头  
        3、见名知义  
        4、统一命名规则:驼峰命名法  
    ● Shell中命名建议规则:  
        1、变量名大写  
        2、局部变量小写  
        3、函数名小写  
        4、用英文名字,并体现出实际作用  
    

    bash中变量的种类

    ● 根据变量的生效范围等标准划分下面变量类型  
        局部变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效  
        环境变量:生效范围为当前shell进程及其子进程  
        本地变量:生效范围为当前shell进程中某代码片断,通常指函数  
        位置变量:$1, $2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数  
        特殊变量:$?, $0, $*, $@, $#,$$  
    

    局部变量

    变量赋值:name=‘value’  
    可以使用引用value  
        (1) 可以是直接字串:name=“root"  
        (2) 变量引用:name="$USER"  
        (3) 命令引用:name=`COMMAND`  
            name=$(COMMAND)  
    变量引用:${name} 或者 $name  
        " " 弱引用,其中的变量引用会被替换为变量值  
        ' ' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串  
    显示已定义的所有变量:set  
    删除变量:unset name  
    

    #创建用户并下次登陆需要修改密码

    #!/bin/bash  
    useradd test  
    echo -e "\033[1;31mUser test is created\033[0m"  
    echo magedu | passwd --stdin test &> /dev/null  
    echo -e "\033[1;31mPasswd: magedu\033[0m"  
    passwd -e test      #登陆后立即改密码  
    
    颜色  
        \033[1,30m  #灰  
        \033[1,31m  #红  
        \033[1,32m  #绿  
        \033[1,33m  #黄  
        \033[1,34m  #蓝  
        \033[1,35m  #粉  
        \033[1,36m  #青  
        \033[1,37m  #白 
        
    bash   
    -n  #检查语法  
    -x  #每一行的执行结果    
    

    练习

    1、 编写脚本 systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址, 操作系统版本,内核版本,CPU型号,内存大小,硬盘大小

    vim systeminfo.sh

    #!/bin/bash  
    COLOR="\033[1;35m"  
    COLOREND="\033[0m"  
    echo -e "OS: $COLOR`cat /etc/redhat-release`$COLOREND"  
    echo -e "Kernel ver: $COLOR`uname -r`$COLOREND"  
    echo -e "IP addr: $COLOR`ifconfig eth0 | grep -Eo '[0-9.]{7,15}' | head -1`$COLOREND"  
    echo -e "CPU: $COLOR`lscpu | grep 'Model name' | cut -d: -f2 | tr -s " "`$COLOREND"  
    echo -e "Disk space: $COLOR`lsblk | grep disk | tr -s " " | cut -d' ' -f4`$COLOREND"  
    echo -e "Memory szie: $COLOR`free -h | grep Mem | tr -s ' ' | cut -d' ' -2`$COLOREND"  
    unset COLOR COLOREND  
    
    2、编写脚本 backup.sh,可实现每日将/etc/目录备份到/backup/etcYYYY-mm-dd中

    vim backup.sh

    #!/bin/bash  
    cp -pr /etc /backup/etc`date +%F`  
    
    3、编写脚本 disk.sh,显示当前硬盘分区中空间利用率最大的值

    vim disk.sh

    #!/bin/bash  
    df -h | tr -s " " % | cut -d% -f5 | tr -s "Use" " " | sort -nr | head -1  
    
    4、编写脚本 links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序

    vim links.sh

    #!/bin/bash  
    netstat -nt | tr -s " " : | cut -d: -f6 | uniq -c | grep -v "Foreign" | sort -nr      
    

    环境变量

    变量声明、赋值:  
        export name=VALUE  
        declare -x name=VALUE  
    变量引用:  
        $name, ${name}  
    显示所有环境变量:  
        env  
        printenv  
        export  
        declare -x  
    删除变量:  
        unset name  
      
    echo $BASHPID   #本shell的进程ID  
    echo $PPID      #父shell的进程ID  
    echo $EDITOR    #默认编辑器  
      
    bash内建的环境变量  
        PATH  
        SHELL  
        USER  
        UID  
        HOME  
        PWD  
        SHLVL       #shell嵌套层数  
        LANG  
        MAIL  
        HOSTNAME  
        HISTSIZE  
        _ 下划线  
    

    只读和位置变量

    只读变量:只能声明,但不能修改和删除  
        声明只读变量:  
            readonly name  
            declare -r name  
        查看只读变量:  
            readonly -p  
    位置变量:在脚本代码中调用通过命令行传递给脚本的参数  
        $1, $2, ... 对应第1、第2等参数,shift [n]换位置  
        $0 命令本身  
        $* 传递给脚本的所有参数,全部参数合为一个字符串  
        $@ 传递给脚本的所有参数,每个参数为独立字符串  
        $# 传递给脚本的参数的个数  
    注意:$@ $* 只在被双引号包起来的时候才会有差异  
        set -- 清空所有位置变量  
      
        ()      #只生效一次,相当于开启一个子shell  
        {}      #不开启子shell  
            (umask 066;touch f1.txt)  
          
        TITLE=ceo  
        ( echo $TITLE;TITILE=coo;echo $TITLE )  
        echo $TITLE  
      
    vim scp.sh  
        #!/bin/bash  
        echo "uploading......"  
        scp -r $* wang@172.16.128.163:/data/script37  
        echo 'upload complete!'  
    

    退出状态

    进程使用退出状态来报告成功或失败  
        0 代表成功,1-255代表失败  
        $? 变量保存最近的命令退出状态  
    例如:  
        ping -c1 -W1 hostdown &> /dev/null  
        echo $?  
    

    退出状态码

    bash自定义退出状态码  
        exit [n]:自定义退出状态码  
        注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字  
        注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码        
    

    算术运算

    bash中的算术运算:help let  
        +, -, *, /, %取模(取余), **(乘方),乘法符号有些场景中需要转义  
        实现算术运算:  
        (1) let var=算术表达式  
        (2) var=$[算术表达式]  
        (3) var=$((算术表达式))  
        (4) var=$(expr arg1 arg2 arg3 ...)  
        (5) declare –i var = 数值  
        (6) echo ‘算术表达式’ | bc  
    bash有内建的随机数生成器变量:$RANDOM(0-32767)  
        示例:生成 0 - 49 之间随机数  
        echo $[$RANDOM%50]    
    

    赋值

    增强型赋值:  
        +=, -=, *=, /=, %=  
    let varOPERvalue  
        例如:let count+=3  
        自加3后自赋值  
    自增,自减:  
        let var+=1  
        let var++  
        let var-=1  
        let var--  
    

    练习

    1、编写脚本 sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的UID之和
    #!/bin/bash  
    UID10=`cat /etc/passwd | head | tail -1 | cut -d: -f3`  
    UID20=`cat /etc/passwd | head -20 | tail -1 | cut -d: -f3`  
    echo "UID10+UID20 = `echo $UID10+$UID20 | bc`"   
    
    2、编写脚本 sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和
    #!/bin/bash  
    FILE1=`grep "^$" $1 | wc -l`  
    FILE2=`grep "^$" $2 | wc -l`  
    NUM=$[ $FILE1+$FILE2 ]  
    echo "两个文件的空行之和:$NUM"         
    
    3、编写脚本 sumfile.sh,统计/etc, /var, /usr 目录中共有多少个一级子目录和文件
    #!/bin/bash  
    DIRS=`ls -ld /{etc,var,usr}/* | grep "^d" | wc -l`                                                                             
    FILES=`ls -ld /{etc,var,usr}/* | grep -v "^d" | wc -l`  
    echo "Directory: $DIRS FILES: $FILES"         
    

    逻辑运算

    true, false  
        1, 0  
    与  
        1 与 1 = 1  
        1 与 0 = 0  
        0 与 1 = 0  
        0 与 0 = 0  
    或  
        1 或 1 = 1  
        1 或 0 = 1  
        0 或 1 = 1  
        0 或 0 = 0  
    

    逻辑运算

    非:!  
        ! 1 = 0 ! true  
        ! 0 = 1 ! false  
    短路运算  
        短路与(&&)  
        第一个为0,结果必定为0  
        第一个为1,第二个必须要参与运算  
        短路或(||)  
        第一个为1,结果必定为1  
        第一个为0,第二个必须要参与运算  
    异或:^  
        异或的两个值,相同为假,不同为真  
          
    x=10;y=12;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x;echo$ y=$y  
        结果为:x=12,y=10  
          
    短路与  
        cmd1 && cmd2        #cmd1正确执行才执行cmd2,cmd1错误执行则不执行cmd2  
          
    短路或  
        cmd1 || cmd2        #cmd1执行错误才执行cmd2          
    

    条件测试

    判断某需求是否满足,需要由测试机制来实现  
        专用的测试表达式需要由测试命令辅助完成测试过程  
    评估布尔声明,以便用在条件性执行中  
        • 若真,则返回0  
        • 若假,则返回1  
    测试命令:  
        • test EXPRESSION  
        • [ EXPRESSION ]  
        • [[ EXPRESSION ]]  
        注意:EXPRESSION前后必须有空白字符  
    

    bash的数值测试

    -v VAR  
        变量VAR是否设置  
    数值测试:  
        -gt 是否大于  
        -ge 是否大于等于  
        -eq 是否等于  
        -ne 是否不等于  
        -lt 是否小于  
        -le 是否小于等于    
    

    bash的字符串测试

    字符串测试:  
        = 是否等于  
        > ascii码是否大于ascii码  
        < 是否小于  
        != 是否不等于  
        =~ 左侧字符串是否能够被右侧的PATTERN所匹配  
        注意: 此表达式一般用于[[ ]]中;扩展的正则表达式  
        -z "STRING“ 字符串是否为空,空为真,不空为假  
        -n "STRING“ 字符串是否不空,不空为真,空为假  
    注意:用于字符串比较时的用到的操作数都应该使用引号  
    

    Bash的文件测试

    存在性测试  
        -a FILE:同 -e  
        -e FILE: 文件存在性测试,存在为真,否则为假  
    存在性及类别测试  
        -b FILE:是否存在且为块设备文件  
        -c FILE:是否存在且为字符设备文件  
        -d FILE:是否存在且为目录文件  
        -f FILE:是否存在且为普通文件  
        -h FILE 或 -L FILE:存在且为符号链接文件  
        -p FILE:是否存在且为命名管道文件  
        -S FILE:是否存在且为套接字文件  
          
    FILE=f.sh; [[ "$FILE" =~ \.sh$ ]] && echo sh    #判断.sh后缀  
      
    ip:0-255.0-255.0-255.0-255  
        正则表达式  
            IP=^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$  
        或  
            IP=^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$  
        ifconfig | grep -Eo "$IP"  
          
    #判断后缀是否为.sh,并判断是否有执行权限  
    FILE=/data/test.sh; [[ "$FILE" =~ \.sh$ ]] && [ -x "$FILE" ] && $FILE  
    

    Bash的文件权限测试

    文件权限测试:  
        -r FILE:是否存在且可读  
        -w FILE: 是否存在且可写  
        -x FILE: 是否存在且可执行  
    文件特殊权限测试:  
        -u FILE:是否存在且拥有suid权限  
        -g FILE:是否存在且拥有sgid权限  
        -k FILE:是否存在且拥有sticky权限  
          
        vim user08.sh  
        #/bin/bash  
        [ "$#" -eq 0 ] && echo "Username is empty" && exit 2  
        id "$1" &> /dev/null && echo "$1 is exist" && exit 3   
        useradd "$1" &> /dev/null && echo "$1 is create" || exit 4  
        echo magedu | passwd --stdin "$1" &> /dev/null || exit 5  
        passwd -e "$1" &> /dev/null || exit 6  
    

    Bash的文件属性测试

    文件大小测试:  
        -s FILE: 是否存在且非空  
    文件是否打开:  
        -t fd: fd 文件描述符是否在某终端已经打开  
        -N FILE:文件自从上一次被读取之后是否被修改过  
        -O FILE:当前有效用户是否为文件属主  
        -G FILE:当前有效用户是否为文件属组  
    

    Bash的文件属性测试

    双目测试:  
        FILE1 -ef FILE2: FILE1是否是FILE2的硬链接  
        FILE1 -nt FILE2: FILE1是否新于FILE2(mtime)  
        FILE1 -ot FILE2: FILE1是否旧于FILE2   
    

    Bash的组合测试条件

    第一种方式:  
        EXPRESSION1 -a EXPRESSION2 并且  
        EXPRESSION1 -o EXPRESSION2 或者  
        ! EXPRESSION  
        必须使用测试命令进行,[[ ]] 不支持  
    第二种方式:  
        COMMAND1 && COMMAND2 并且,短路与,代表条件性的AND THEN  
        COMMAND1 || COMMAND2 或者,短路或,代表条件性的OR ELSE  
        ! COMMAND 非  
        如:[ -f “$FILE” ] && [[ “$FILE”=~ .*\.sh$ ]]  
    

    练习

    1、 编写脚本 argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1, 则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
    #!/bin/bash  
    SpaceLine=`grep '^$' $1 | wc -l`                                                                                                                               
    [ "$#" -lt 1 ] &&  echo "至少应该给一个参数" && exit 2  
    [ -s "$1" ] && echo "$1 Space Lines:$SpaceLine" || echo "$1 is not exist"  
    
    2、 编写脚本 hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连
    通。如果能ping通,则提示用户“该IP地址可访问” ;如果不可ping通,则提示用户“该IP地址不可访问”  
          
    #!/bin/bash  
    IP="192.168.139.2"  
    ping -c1 $IP &> /dev/null  
    [ "$?" -eq 0 ] && echo "${IP} 地址可访问"  || echo "${IP} 地址不可访问"  
    
    3、 编写脚本 checkdisk.sh,检查磁盘分区空间和inode使用率,如果超过80%,就发广播警告空间将满
    #!/bin/bash  
    SPACE=80  
    DISK=`df -h | grep "/dev/sd" | tr -s " " % | cut -d% -f5 | sort -nr | head -1`  
    [ $DISK -ge $SPACE ] && echo "Warring: `hostname` the disk space is $DISK" || echo "`hostname` the disk is Normol"  
    
    4、 编写脚本 per.sh,判断当前用户对指定参数文件,是否不可读并且不可写
    #!/bin/bash  
    [ "$1" = "" ] && echo "Please input the file" && exit 2                                                                     
    [ ! -f "$1" ] && echo "File is not exist" && exit 3  
    [ -r "$1" -a -w "$1" ] && echo "`whoami` is read and write." || echo "`whoami` is not read and wrtie."  
    
    5、 编写脚本 excute.sh ,判断参数文件是否为sh后缀的普通文件,如果是,添加所有人可执行权限,否则提示用户非脚本文件
    #!/bin/bash  
    [ "$#" -eq 0  ] && echo "Please Input file's name" && exit 2                                                                   
    [[ "$1" =~ \.sh$ ]] && [ -f "$1" ] && chmod +x $1 && echo "Excute is added" || echo "The file is not script."   
    
    6、 编写脚本 nologin.sh和 login.sh,实现禁止和允许普通用户登录系统

    vim nologin.sh

    #!/bin/bash  
    [ "$1" = "" ] && echo "Please input username" && exit 2  
    [[ "$1" =~ ^[0-9] ]] && echo "The username is wrong" && exit 3  
    id $1 &> /dev/null  
    [ "$?" -ne 0 ]  && echo "$1 is not exist" && exit 4   
    passwd -l $1 &> /dev/null && echo "$1 is lock"  
    

    vim login.sh

    #!/bin/bash  
    [ "$1" = "" ] && echo "Please input username" && exit 2  
    [[ "$1" =~ ^[0-9] ]] && echo "The username is wrong" && exit 3  
    id $1 &> /dev/null  
    [ "$?" -ne 0 ]  && echo "$1 is not exist" && exit 4   
    passwd -u $1 &> /dev/null && echo "$1 is unlock"              
    

    使用read命令来接受输入

    使用read来把输入值分配给一个或多个shell变量  
        -p 指定要显示的提示  
        -s 静默输入,一般用于密码  
        -n N 指定输入的字符长度N  
        -d ‘字符’ 输入结束符  
        -t N TIMEOUT为N秒  
        read 从标准输入中读取值,给每个单词分配一个变量  
        所有剩余单词都被分配给最后一个变量  
        read -p “Enter a filename: “ FILE  
      
    read 变量1 变量2  
        read -p "Please input your name: " NAME  
        echo Your name is $NAME       
      
        echo 1 2 3 | { read x y z ; echo $x $y $z; }        #管道改变变量是在子shell里面,通过{}可以不开子shell  
    

    条件选择if语句

    选择执行:  
    注意:if语句可嵌套  
    单分支  
        if 判断条件;then  
            条件为真的分支代码  
        fi  
    双分支  
        if 判断条件; then  
            条件为真的分支代码  
        else  
            条件为假的分支代码  
        fi  
    

    if 语句

    多分支  
        if 判断条件1; then  
            条件1为真的分支代码  
        elif 判断条件2; then  
            条件2为真的分支代码  
        elif 判断条件3; then  
            条件3为真的分支代码  
        else  
            以上条件都为假的分支代码  
        fi  
    逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句  
    

    条件判断:case语句

    case 变量引用 in  
        PAT1)  
            分支1  
        ;;  
        PAT2)  
            分支2  
        ;;  
        ...  
        *)  
            默认分支  
        ;;  
    esac  
      
    case支持glob风格的通配符:  
        *: 任意长度任意字符  
        ?: 任意单个字符  
        []:指定范围内的任意单个字符  
        a|b: a或b  
    

    练习

    1、编写脚本 createuser.sh,实现如下功能:使用一个用户名做为参数,如果 指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
    #!/bin/bash  
    read -p "Please input the username: " USERNAME  
    id $USERNAME &> /dev/null  
    if [ "$?" -eq 0 ];then  
        echo "User is exist"  
        exit 2  
    elif [[ "$USERNAME" =~ ^[0-9] ]];then                                                                                       
        echo "Username is not right"  
        exit 3  
    else  
        useradd $USERNAME &> /dev/null  
        id $USERNAME  
    fi        
    
    2、编写脚本 yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息
    #!/bin/bash  
    read -p "Please input yes or no: " VALUE  
    case $VALUE in                                                                                                              
        [Yy]|[Yy][Ee][Ss])  
            echo yes  
        ;;  
        [Nn]|[Nn][Oo])  
            echo no  
        ;;  
        *)  
            echo "Input error"  
        ;;  
    esac  
    
    3、编写脚本 filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)
    #!/bin/bash  
    read -p "Please input path: " PATH  
    if [ "$PATH" = "" ];then  
        echo "No such file or directory"  
        exit 2  
    elif [ -d "$PATH" -o -f "$PATH" ];then                                                                                      
        /usr/bin/ls -l $PATH  
    else  
        echo "Please input vaild path"  
    fi        
    
    4、编写脚本 checkint.sh,判断用户输入的参数是否为正整数
    #!/bin/bash  
    read -p "Please input digit: " NUM  
    if [[ "$NUM" =~ ^[0-9]+$ ]];then  
        /usr/bin/expr $NUM + 0 &> /dev/null                                                                                     
        [ "$?" -eq 0 ] && echo "It is integer" || echo "It is not integer"  
    elif [[ "$NUM" =~ [.] ]];then  
        echo "It like not a integer"  
    elif [[ "$NUM" =~ ^-[0-9]+$  ]];then  
        echo "It like a negative integer "  
    else  
        echo "It like not digit"  
        exit 2  
    fi  
    

    bash如何展开命令行

    ● 把命令行分成单个命令词  
    ● 展开别名  
    ● 展开大括号的声明({})  
    ● 展开波浪符声明(~)  
    ● 命令替换$() 和 ``)  
    ● 再次把命令行分成命令词  
    ● 展开文件通配(*、 ?、 [abc]等等)  
    ● 准备I/0重导向(<、 >)  
    ● 运行命令  
    

    防止扩展

    反斜线(\)会使随后的字符按原意解释  
        echo Your cost: \$5.00  
        Your cost: $5.00  
    加引号来防止扩展  
        • 单引号(’’ )防止所有扩展  
        • 双引号(” “)也可防止扩展,但是以下情况例外:  
        $(美元符号) 变量扩展  
        ` ` (反引号) 命令替换  
        \(反斜线) 禁止单个字符扩展  
        !(叹号) 历史命令替换  
    

    bash的配置文件

    按生效范围划分,存在两类:  
    全局配置:  
        /etc/profile  
        /etc/profile.d/*.sh  
        /etc/bashrc  
    个人配置:  
        ~/.bash_profile  
        ~/.bashrc  
    

    shell登录两种方式

    交互式登录:  
        (1)直接通过终端输入账号密码登录  
        (2)使用“su - UserName” 切换的用户  
    执行顺序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile -->  
    ~/.bashrc --> /etc/bashrc  
    非交互式登录:  
        (1)su UserName  
        (2)图形界面下打开的终端  
        (3)执行脚本  
        (4)任何其它的bash实例  
    执行顺序: /etc/profile.d/*.sh --> /etc/bashrc -->~/.bashrc  
    

    Profile类

    按功能划分,存在两类:  
        profile类和bashrc类  
    profile类:为交互式登录的shell提供配置  
        全局:/etc/profile, /etc/profile.d/*.sh  
        个人:~/.bash_profile  
        功用:  
        (1) 用于定义环境变量  
        (2) 运行命令或脚本  
    

    Bashrc类

    bashrc类:为非交互式和交互式登录的shell提供配置  
        全局:/etc/bashrc  
        个人:~/.bashrc  
    功用:  
        (1) 定义命令别名和函数  
        (2) 定义本地变量  
    

    编辑配置文件生效

    修改profile和bashrc文件后需生效  
    两种方法:  
        1重新启动shell进程  
        2 . 或source  
    例:  
        . ~/.bashrc   
    

    Bash 退出任务

    保存在~/.bash_logout文件中(用户)  
    在退出登录shell时运行  
    用于  
        创建自动备份  
        清除临时文件  
    

    set命令

    $- 变量  
    h:hashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭  
    i:interactive-comments,包含这个选项说明当前的 shell是一个交互式的shell。所谓的交互式shell,在脚本中,i选项是关闭的。  
    m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。  
    B:braceexpand,大括号扩展  
    H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成  
        例如“!!” 返回上最近的一个历史命令,“!n” 返回第 n 个历史命令  
    

    脚本安全

    set 命令  
        -u 在扩展一个没有设置的变量时,显示错误信息  
            等同set -o nounset  
        -e 如果一个命令返回一个非0退出状态值(失败)就退出  
            等同set -o errexit  
              
        -u  #变量没声明不能用  
        -e  #有错误就退出不执行后面的语句  
          
        set +o history  #不记录历史  
        set -o history  #记录历史  
    

    练习

    1、让所有用户的PATH环境变量的值多出一个路径,例如:/usr/local/apache/bin

    vim /etc/profile.d/env.sh

    PATH=$PATH:/usr/local/apache/bin  
    
    2、用户 root 登录时,将命令指示符变成红色,并自动启用如下别名:

    vim ~/.bashrc

    rm='rm –i'  
    cdnet='cd /etc/sysconfig/network-scripts/'  
    editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'  
    #editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno16777736 或 ifcfg-ens33' (如果系统是CentOS7)  
      
    VERSION=`grep -Eo "\<[0-9]+\>" /etc/redhat-release | head -1`  
    PS1="\[\e[31m\][\u@\h \W]\\$\[\e[0m\]"  
    alias rm='rm –i'  
    alias cdnet='cd /etc/sysconfig/network-scripts/'              
    case $VERSION in  
        6)  
            alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'                     
        ;;  
        7|8)  
            alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno33'  
        ;;  
        *)                    
        ;;  
    esac  
    
    3、任意用户登录系统时,显示红色字体的警示提醒信息“Hi,dangerous!”

    vim /etc/motd

    ^[[1;34mHi,dangerous!   #^[等于ascii码的1B,编辑模式按ctrl+v再按ESC可以打出来  
    
    4、编写生成脚本基本格式的脚本,包括作者,联系方式,版本,时间,描述等

    vim ~/.vimrc

    set tabstop=4         
    set ignorecase        
    set cursorline        
    set autoindent        
    autocmd BufNewFile *.sh exec ":call SetTitle()"  
    func SetTitle()  
        if expand("%:e") == 'sh'  
            call setline(1,"#!/bin/bash")   
            call setline(2,"#")   
            call setline(3,"#********************************************************************")   
            call setline(4,"#Author:        LiangJC")   
            call setline(5,"#QQ:            184116857")   
            call setline(6,"#Date:          ".strftime("%Y-%m-%d"))  
            call setline(7,"#FileName:      ".expand("%"))  
            call setline(8,"#URL:           http://www.magedu.com")  
            call setline(9,"#Description:       The test script")   
            call setline(10,"#Copyright (C):    ".strftime("%Y")." All rights reserved")  
            call setline(11,"#********************************************************************")   
            call setline(12,"")   
        endif  
    endfunc  
    autocmd BufNewFile * normal G  
    
    5、编写用户的环境初始化脚本reset.sh,包括别名,登录提示符,vim的设置,环境变量等
    #!/bin/bash       
    #环境变量  
    echo "PATH=$PATH:/usr/local/apache/bin &>> /etc/profile.d/env.sh"  
      
    #提示符及别名  
    VERSION=`grep -Eo "\<[0-9]+\>" /etc/redhat-release | head -1`  
    PS1="\[\e[31m\][\u@\h \W]\\$\[\e[0m\]"  
    alias rm='rm –i'  
    alias cdnet='cd /etc/sysconfig/network-scripts/'              
    case $VERSION in  
        6)  
            alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'                     
        ;;  
        7|8)  
            alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno33'  
        ;;  
        *)                    
        ;;  
    esac  
      
    #用户登陆信息  
    echo "^[[1;34mHi,dangerous!" > /etc/motd  
      
    #vim格式  
    cat > /etc/skel/.vimrc <<EOF  
    set tabstop=4         
    set ignorecase        
    set cursorline        
    set autoindent        
    autocmd BufNewFile *.sh exec ":call SetTitle()"  
    func SetTitle()  
        if expand("%:e") == 'sh'  
            call setline(1,"#!/bin/bash")   
            call setline(2,"#")   
            call setline(3,"#********************************************************************")   
            call setline(4,"#Author:        LiangJC")   
            call setline(5,"#QQ:            184116857")   
            call setline(6,"#Date:          ".strftime("%Y-%m-%d"))  
            call setline(7,"#FileName:      ".expand("%"))  
            call setline(8,"#URL:           http://www.magedu.com")  
            call setline(9,"#Description:       The test script")   
            call setline(10,"#Copyright (C):    ".strftime("%Y")." All rights reserved")  
            call setline(11,"#********************************************************************")   
            call setline(12,"")   
        endif  
    endfunc  
    autocmd BufNewFile * normal G  
    EOF  
    USERNAME=`whoami`  
    cp -a /etc/skel/.vimrc /$USERNAME/  
      
    echo "初始化成功!"    
    

    相关文章

      网友评论

        本文标题:8-shell脚本基础

        本文链接:https://www.haomeiwen.com/subject/tklofhtx.html