shell脚本编程

过程式编程,解释执行,依赖于外部程序文件运行;

脚本是什么:
    命令的堆积;
    但很多命令不具有幂等性,需要用程序逻辑来判断运行条件是否满足;
    
脚本的用途:
    自动化常用命令;
    执行系统管理和故障排除;
    创建简单的应用程序;
    处理文本或文件;            

编程语言的分类    
    根据运行方式:
        编译运行:源代码-->编译器(编译)-->程序文件
        解释运行:源代码-->运行时启动解释器,由解释器边解释边运行
    
    根据其编程过程中功能的实现是调用库还是调用外部的程序文件:
        shell脚本编程:
            利用系统上的命令及编程组件进行编程;
        完整编程:
            利用库或编程组件进行编程;
        
        程序编译方式:
            动态链接;
            静态编译;
            
    根据编程模型:
        过程式编程语言;
        面向对象的编程语言;
    
        程序=指令+数据
        过程式:以指令为中心来组织代码,数据是服务于代码;
            顺序执行
            选择执行
                代码只有一个分支时:条件满足时才执行;(if)
                两个或以上的分支,只会执行其中一个满足条件的分支;(case)
            循环执行
            代表:C,bash
        对象式:以数据为中心来组织代码,围绕数据来组织指令;
            类(class):实例化对象,method;
            代表:java,C++,Python
            
程序的组成部分:二进制程序文件、库文件、配置文件、帮助文件;
    二进制、库文件: 可执行文件;
        库文件:不能独立执行,只能被调用时执行;
    配置文件、帮助文件:可被查看的文本文件;
        《穿越计算机的迷雾》、《量子物理史话》
    
    编程语言的基本结构:
        数据存储:变量、数组
        表达式:a+b
        语句:if
        
如何编写shell脚本:
    第一步:使用文本编辑器来创建文本文件;        
        文本编辑器:
        行编辑器:sed
        全屏幕编辑器:nano,vi,vim
        
        第一行顶格:给出shebang,解释器路径,用于指明解释执行当前脚本的解释器程序文件;
        常见的解释器:
            #!/bin/bash
            #!/usr/bin/python
            #!/usr/bin/perl    
            
        添加注释,注释以#开头;
        缩进
        适度添加空白行;
        
        脚本中的空白行会被解释器忽略;
        脚本中除了shebang,余下所有以#开头的行都会被视作注释行而被忽略;
                    
    第二步:执行脚本:
        脚本的运行是通过运行一个子shell进程实现的;
        (1)赋予执行权限,并直接运行此程序文件;在命令行上指定脚本的绝对或相对路径;
            chmod +x /PATH/TO/SCRIPT_FILE
            /PATH/TO/SCRIPT_FILE
                对脚本有rx权限;
                对脚本所在的目录有rx权限;
        
        (2)直接运行解释器,将脚本作为解释器程序的参数运行;
            bash /PATH/TO/SCRIPT_FILE
                对脚本有r权限;
                对脚本所在的目录有rx权限;
                
        (3)点号用于执行某个脚本,甚至脚本没有可执行权限也可以运行;
           如果没有执行权限,用"./"执行就会报权限错误,但是在前面使用点号执行就不会报错;
            #. ./99.sh
            
        (4)souce命令也可以读取并在当前环境中执行脚本,同时还返回脚本中最后一个命令的返回状态;
           如果没有返回值则为0,代表执行成功;如果执行失败则返回false.    
            #source 9.sh    
                            
    1、编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名、IPv4地址、操作系统版本、内核版本、CPU型号、内存大小、硬盘大小;
        vim /root/bin/systeminfo.sh
        #!/bin/bash
        echo "hostname:`hostname`"
        echo "ip address is:`ifconfig eno16777736 | head -2 | tail -1 | tr -s " " | cut -d" " -f3`"
        echo "os release version:`cat /etc/centos-release`"
        echo "kernel version:`uname -r`"
        echo "cpu model:`cat /proc/cpuinfo | grep "model name" | uniq | cut -d: -f2`"
        echo "memery size:`cat /proc/meminfo | grep -i "memtotal" | tr -s " " | cut -d: -f2`"
        echo "disk size:`fdisk -l /dev/sda | head -2 | tail -1 | cut -d: -f2`"
        
    2、编写脚本/root/bin/backup.sh,可实现将/etc/目录备份到/root/etcYYYY-mm-dd中;
        vim /root/bin/backup.sh
        #!/bin/bash
        cp -a /etc /root/etc`date +%F`
        
    3、编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值;
        vim /root/bin/disk.sh
        #!/bin/bash
        df | tr -s " " | grep "sd" | sort -nr -k5 | head -1 | cut -d" " -f5
        
    4、编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序;
        vim /root/bin/links.sh
        #!/bin/bash
        netstat -tan | tr -s " " | cut -d" " -f5 | grep -v ".*[[:alpha:]]\+.*" | uniq -c | sort -r -k1
        或者netstat -tan | tr -s " " | cut -d" " -f5 | tail -n +3 | uniq -c | sort -r    
        
    5、写一个脚本,实现如下功能:
       (1)显示/etc/目录下所有以大写P或小写p开头的文件或目录本身;
       (2)显示/var/目录下的所有文件或目录本身,并将显示结果中的小写字幕转换为大写字母后显示;
       (3)创建临时文件/tmp/myfile.XXXX;
        vim test.sh
        #!/bin/bash
        ls -d /etc/[Pp]*
        ls -d /var/* | tr 'a-z' 'A-Z'            
        mktemp /tmp/myfile.XXXX            
        chmod +x test.sh    
        
read命令:用户交互;
    从键盘读入数据,赋给变量或数组;
    read -p "Enter a filename:" FILE    
        -p 'promt' 给变量赋值;在尝试读取之前,输出PROMPT提示符,并且不换行;
        -t TIMEOUT 超时;
        -a array 给数组赋值;
    
        read -t 5 -p "Enter a username: " name
        [ -z "$name" ] && name="obama"
        echo $name    
        
shift:向左移动位置参数;
    shift[n]:位置参数轮替;向左移动位置参数;
    假设脚本有A,B,C三个参数,那么$1为A,$2为B,$3为C;shift一次之后,$1为B,$2为C;再shift一次后,$1为C...    
    vim shift.sh
    #!/bin/bash
    echo "First pos argu: $1"
    shift
    echo "Second pos argu: $1"
    
exit:退出本次登录;
    在脚本中,exit代表退出当前脚本;可以跟一个数字参数,代表退出状态;
    
break:
    跳出循环;从一个循环(for,while,until,select)中退出;
    break后面可以跟一个数字n,代表跳出n层循环,n必须大于1,如果n比当前循环层数还大,则跳出所有循环;
    break: only meaningful in a 'for', 'while', or 'until' loop
    vim break01.sh
    #!/bin/bash
    for I in A B C D
    do
        echo -n "$I:"
        for J in `seq 10`
        do
            if [ $J -eq 5 ];then
                break
            fi
            echo -n " $J"
        done
        echo
    done
    当J值为5时,break的输出结果:(循环了4次)
    A: 1 2 3 4
    B: 1 2 3 4
    C: 1 2 3 4
    D: 1 2 3 4
            
    vim break02.sh
    #!/bin/bash
    for I in A B C D
    do
        echo -n "$I:"
        for J in `seq 10`
        do
            if [ $J -eq 5 ];then
                break 2
            fi
            echo -n " $J"
        done
        echo
    done
    echo
    当J值为5时,break 2的输出结果:(循环了1次)
    A: 1 2 3 4
    
continue:
    结束当前循环而进入下一次循环;
    continue后面还可以跟一个数字n,代表跳至外部第n层循环,n必须大于1,如果n比当前循环层数还大,将跳至最外层的循环;
    和break不同的是,continue不会终止整个循环体,它只是提前结束本次循环;而整个循环体还将继续执行;
    continue: only meaningful in a 'for', 'while', or 'until' loop
    vim continue.sh
    #!/bin/bash
    for I in A B C D
    do
        echo -n "$I:"
        for J in `seq 10`
        do
            if [ $J -eq 5 ];then
                continue
            fi
            echo -n " $J"
        done
        echo
    done
    当J值为5时,continue的输出结果:
    A: 1 2 3 4 6 7 8 9 10
    B: 1 2 3 4 6 7 8 9 10
    C: 1 2 3 4 6 7 8 9 10
    D: 1 2 3 4 6 7 8 9 10
    
    vim continue.sh
    #!/bin/bash
    for I in A B C D
    do
        echo -n "$I:"
        for J in `seq 10`
        do
            if [ $J -eq 5 ];then
                echo
                continue 2
            fi
            echo -n " $J"
        done
        echo
    done
    当J值为5时,continue 2的输出结果:
    A: 1 2 3 4
    B: 1 2 3 4
    C: 1 2 3 4
    D: 1 2 3 4
    
    #!/bin/bash
    while true
    do
        read -p "Select 1 or 2: " num
        echo 1
        sleep 1
        if [ $num -eq 1 ];then
            continue
        fi
        echo 2
    done

    #/bin/bash
    while true
    do
        read -p "Select 1 or 2: " num
        echo 1
        sleep 1
        if [ $num -eq 1 ];then
            break
        fi
        echo 2
    done
    echo 3