shell脚本基础

编写脚本
    vim script.sh
    #!/bin/bash(env/pathon)  shebang机制,
    变量名=值  
        等号与值之间有空格,比较
        等号与值之间无空格,赋值
    调用:echo "...$变量名"
        不加$,打印字符串
        加$,打印值
        加$,系统能够判断出这是个变量
    chmod +x script.sh 
    bash -n script.sh 
    bash -x script.sh
    ./script.sh or /root/script.sh 
        绝对路径或者放入path变量路径
编程基础
    程序:指令+数据
    程序编程风格
        过程式:指令为中心,数据服务于指令
        对象式:数据为中心,指令服务于数据
    shell程序:提供编程能力,解释执行
    程序执行方式
        计算机:二进制指令
        编程语言 低级 高级
            编译:高级语言 编译器 目标代码 java/c++
            解释:高级语言 解释器 机器代码 shell/python/perl
    编程逻辑处理方式
        顺序执行
        循环执行
        选择执行
    shell编程
        过程式 解释执行 
        编程语言的基本结构
            各种系统命令的组合
            数据存储:变量/数组
            表达式:a+b 
            语句:if
shell脚本
    包含一些命令和声明,并符合一定格式的文本文件
    格式要求:首行是shebang机制
        #!/bin/bash
        #!/usr/bin/python
        #!/usr/bin/perl
    用途
        自动化常用命令
        执行系统管理和故障排查
        创建简单的应用程序
        处理文本和文件
创建shell脚本
        第一,文本编辑器来创建文件
            vim hello.sh 
                #!/bin/bash  使用调用的语言
                #filename:hello.sh 
                #version:2.0
                #date:2017-10-20  更改后的时间
                #author:fenggenqiang  作者信息
                #discription:This is for ...  该程序的作用和注意事项
                #各种版本的更新简要说明
            脚本调试
                bash -n /path/to/script.sh  检查脚本中的语法错误(systax error)
                bash -x /path/to/script.sh  显示脚本执行cmd的结果
        第二,运行脚本
            给予执行权限,在命令行上指定脚本的绝对或相对路径(一般,都要加X权限)
            chmod +x hello.sh 
            或者 bash hello.sh
            或者 ./source hello.sh 
            hello.sh不在PATH变量里
                /root/hello.sh 写绝对路径
                mv hello.sh bin/ ; hello.sh (注意:cd /bin 和 cd bin/ 二者不同)
                或者 mv /root/bin/hello.sh /usr/local/bin/ ,
                    移动之后,hello.sh找不到路径,hash -d hello.sh 删除缓存即可(hash -r 删除所有缓存)
    不同的软链接,对应相同内容,但执行效果不同
        a1.sh —— a.sh
        a2.sh —— a.sh 
        #!/bin/bash
            echo ...
            xxx
            if   syntax error,阻止后续的执行
            echo "continue"
        #!/bin/bash
            echo ...
            xxx  cmd error,可以向后继续执行
            echo "continue"
        编译中出现语法错误,就不会执行(转化为2进制)
    解释器不同,打开子进程不同
        echo $$  显示当前进程编号
        echo $PID  显示父进程,系统自带,非定义变量
        pstree (-p)  显示全部进程编号
        hello.sh 
            #!...
            echo "..."
            f2.sh(调用文件)
        f2.sh
            #!...
            echo "..." 
            sleep 100(调用命令)
        hello.sh调用f2.sh,f2.sh调用sleep
        bash hello.sh 在bash进程中,打开脚本(hello.sh)  bash——bash——f2.sh——sleep
        ./source hello.sh 在当前进程中,打开脚本  bash——f2.sh——sleep
        hello.sh 在脚本进程中打开脚本 bash——hello.sh——f2.sh——sleep
    bash+脚本(bash hello.sh) 
        or 脚本(hello.sh) bash或脚本执行调用,将以子进程方式运行
    source+脚本 ./source 脚本,将在当前shell运行;恐更改当前工作环境
变量
    命名的内存空间
    数据存储方式
        字符
        数值 整型/浮点型(小数)
    作用
        数据存储格式
        参与的运算
        表示数据范围
    类型
        强类型
            变量不经过强制转换,永远是个数据类型,不允许隐式的类型转换
            定义变量——指定类型
            参与运算——符合类型要求
            调用未声明的变量——产生错误
            JAVA C++
        弱类型
            语言运行——会隐式做数据类型转换
            无须指定类型,默认为字符型;(数据也是字符,加“”,可显示数字)
            参与运算——自动进行隐式类型转换
            变量无需事先定义,可直接调用
            php bash不支持浮点型
    变量命名规则
        不能使用程序中的保留字:if/for
        只能使用数字,字母,下划线,且不能以数字开头 name1√ 1name×
        见名知义
        统一命名规则:驼峰命名法 第一个字母(单词)
            大驼峰StudentNameFirst...  JAVA类变量
            小驼峰studentNameFirst...
    bash中的变量种类(生效范围)
        本地变量
            生效范围:当前shell进程
            变量赋值:name='value'
                name="root" 字串
                name="$USER" 变量引用
                name=`cmd` 命令引用  ‘...’弱引用,不识别变量引用
            set 显示已经定义的所有变量
            unset name  删除变量,不用加$           
        环境变量
            生效范围:当前shell进程及其子进程
            变量声明/赋值:
                export name=value
                declare -x name=value
            变量引用:
                $name/${name}
            env/printenv/export/declare -x  显示所有环境变量
            unset name  删除变量,不用加$
            bash内建的环境变量:
                    PATH/SHELL/USER/UID/HOME/PWD/SHLVL(显示层级)/LANG/MAIL/HOSTNAME/HISTSIZE
                echo $变量名
                     sshd——bash——bash——bash 
                echo $SHELL  1    2     3
                
                举例
                name=father;echo $name
                name="father god"; echo $name,有空格,需要加“”
                userinfo=`who`;echo "$userinfo",cmd加``,引用cmd输出
                father.sh 
                    #!/bin/bash 
                    title=boss(执行,报错:本地变量) |export title=boss(可以执行:环境变量)
                    echo "father.sh:name=$title"
                    son.sh 
                son.sh 
                    #!/bin/bash
                    echo "son.sh:name=$title"
                本地变量:子进程不能用父进程的变量
                环境变量:子进程可以用父进程的变量;但是父进程不能用子进程的变量;
                
        局部变量
            生效范围:当前shell进程中的某代码片段(常指函数)
        位置变量
            在脚本代码中调用通过命令行传递给脚本的参数
            $1,$2,...:对应第一,第二,...参数,shift[n]换位置,一脚踢几个
                只读变量
                    只能声明,不能修改和删除
                    声明只读变量
                        readonly name
                        declare -r name
                    查看只读变量
                        readonly -p 
                    PI=3.14159;退出恢复可修改状态
            $0  命令本身
            $*  传递给脚本的所有参数,全部参数合为一个字符串
            $@  传递给脚本的所有参数,每个参数做为独立字符串
            $#  传递给脚本的参数个数
            set --  清空所有位置变量
            $$  当前进程编号
            $PID  父进程,系统自带,非定义变量
            
    练习
        vim /root/bin/systeminfo.sh 
            #!/bin/bash
            Hostname=`hostname`
            echo "主机名是$Hostname"
            Ip=`ifconfig |egrep -o "\<(([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]\>)"`
            echo "IP地址是$Ip"
            Kernel=`uname -r`
            echo "内核版本是$Kernel"
            CPUinfo=`lscpu |grep -i "model name"`
            echo "CPU信息是$CPUinfo"
            Systemversion=`cat /etc/redhat-release`
            echo "系统版本是$Systemversion"
            Roomfree=`free -m|grep Mem|tr -s " " ":"|cut -d: -f4`
            echo "剩余内存空间是$Roomfree"
            Roomused=`df -h|grep "/dev/sd"|tr -s " " "%"|cut -d% -f5`
            echo "已经使用的内存是$Roomused"
            unset Hostname Ip Kernel CPUinfo Systemversion Roomfree Roomused
        vim linkstate.sh
            #!/bin/bash 
            Ipv4_linkstate=`netstat -nt |tr -s " " : |cut -d: -f6 |egrep "([0-9]+.{3}[0-9]+)"|sort|uniq -c`
            echo "IPV4地址的链接状态是$Ipv4_linkstate"
            unset Ipv4_linkstate
        vim etcbak_day.sh
            #!/bin/bash
            today=`date +%F`
            cp -av /etc/ /app/etc$today
            unset today
        vim diskused_max.sh
            #!/bin/bash
            Max_diskused=`df |grep "/dev/sd"|egrep -o "\<[[:digit:]]+%"|tr -d % |sort -n|tail -n 1`
            echo "磁盘使用的最大值是$Max_diskused"
            unset Max_diskused
        vim position_1.sh
            #!/bin/bash
            echo $0  #表示脚本名称本身
            echo $1  #表示第一个参数
            shift    #踢走1个参数
            echo $2
            echo $3
            shift 2  #踢走2个参数
            echo $4
            echo $5
            #set ---  表示下面的内容被清空
            echo \$*:$*
            echo \$@:$@
            echo \$#:$#
            echo "第十个参数是${10},而不是$10"  #注意大于10的,加大括号{} 
          bash position_1.sh  tom jerry obama xoxo gaga lele bibi coco
                  $0           $1    2     3     4   5    6    7    8
            
        echo $?  判断在bash里执行的最近一条命令成功或者失败  0成功 1-255失败
        exit [n]  自定义退出状态码
            脚本一旦遇到exit命令,就会立即终止,后面的命令不会执行
            退出状态取决于exit命令后面的数字
            未给出数字,退出状态码取决于脚本中执行的最后一条命令的状态码
            应用:满足不同的状态,返回不同的编码 如:404-找不到网页
        $#与$*的区别
            a.sh 
                #!/bin/bash
                echo "$*"
                bash b.sh "$*"  #把$*改为$@,则结果不同(a.sh a b c),$*是abc和abc,$@是abc和a
            b.sh 
                #!/bin/bash
                echo "$1"
        ping -c1 192.168.227.132 &> /dev/null || echo $? 
        shift与echo $?结合,判断”参数有无“,位置变量脚本+参数 
            shift n 
            n > $#  失败
            n ≤ $#  成功
        scp 远程主机文件传到本机文件
            本机——>远程主机 
                scp f.sh [email protected]:/app  把本机当前文件传到远程主机192.168.227.133的/app下
                scp -r dir [email protected]:/app  传送文件夹
            远程主机——>本机 
                scp [email protected]:/app/f.sh  /app/  把远程主机192.168.227.133的/app/f.sh 传到本机/app下
            vim scp.sh 
                #!/bin/bash
                Remotehost="[email protected]:/app/"
                scp $* $Remotehost  #$*表示多个参数
                unset Remotehost
              bash /app/scp.sh /app/file1 /app/file2  把/app下的两个文件传到远程主机
        实现自动化生成脚本模板
            cd bin/
            vim Creatscrp.sh 
                [[ -z "$1" ]] && read -p "请输入你要创建的脚本名称:"
                read name 
                Scrpname=$name  
                cat > $name  << EOF
                #!/bin/bash  使用调用的语言
                #filename:hello.sh 
                #version:2.0
                #date:2017-10-20  更改后的时间
                #author:fenggenqiang  作者信息
                #discription:This is for ...  该程序的作用和注意事项
                #各种版本的更新简要说明
                #QQ:xxxxxx
                #微信:xxxxxx
                #手机号:xxxxxx
                EOF
                chmod +x "$name"
                vim + $name  #光标移到最后一行
运算
    算术运算
        help let
        +、-、*、/、%(取余/取模)、**
        *需要转义:\*
        实现计算
            let var=算术表达式
            var=$[算术表达式]
            var=$((算术表达式))
            var=$(expr arg1 arg2 ...)
            declare -i var=数值
            echo '算术表达式' |bc
        bash内置的随机数生成器
            $RANDOM 0-32767
            echo $[$RANDOM%50]  得出的结果是:0-49之间的随机数
        赋值
            增强型赋值  +=、-=、*=、/=、%=
            let count+=3  自己加上3后,给自己赋值
            let var+=1 等于 let var++
            let var-=1 等于 let var--
        练习
            let var=2+5;echo $var
            x=2;y=5;let z=x+y(或者 z=x*y);echo $z 
            var=$[2+4];echo $var
            x=2;y=4;var=$[x+y](或者var=$[x*y]或者var=$[x**y]);echo $var             
            expr 2 + 3  注意空格,+、-、/、\*(乘法需要转义)
            echo $[$RANDOM%6+1]  0-6之间的随机数,玩骰子
            var=0;let var++;let j=var++;echo $j;echo $var  1 2  先赋值再加
            var=0;let ++var;let j=++var;echo $j;echo $var  2 2  先加再赋值
            用户的id之和 vim and_id.sh 
                #!/bin/bash
                user10=`cut -d : -f 3 /etc/passwd | head -10 |tail -1`
                user20=`cut -d : -f 3 /etc/passwd | head -20 |tail -1`
                user_and=$[$user10+$user20]  #此处的变量名不能为user_10,负责识别不出来
                echo $user_and
            空格之和 vim and_spaceline.sh  小bug
                #!/bin/bash
                spaceline1=`grep "^$*" $1 |wc -l`
                spaceline2=`grep "^$*" $2 |wc -l`
                spaceline_and=$[$spaceline1+$spaceline2]
                echo "文件的空行之和为$spaceline_and"
            and_dir.sh
                #!/bin/bash
                dir1=`ls -A /etc |wc -l`
                dir2=`ls -A /var |wc -l`
                dir3=`ls -A /usr |wc -l`
                dirsum=$[$dir1+$dir2+$dir3]
                echo $dirsum
    逻辑运算
            echo $?:0成功 非0失败
            true 1 ,false 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
                !0=1
            cmd1 && cmd2 || cmd3  cmd1为真,执行cmd2;cmd1为假,执行cmd3           
        练习
            #!/bin/bash
            user=$1
            id $user &> /dev/null && echo "$user已经存在" && exit || (useradd $user && echo "$user创建成功")  括号必须要加
            chmod +x *
测试
    条件测试
        测试命令
            test expression
            [ expression ]
            [[ expression ]]  expression前后必须要有空白字符
            echo $?  若真为0,若假为1
            变量加引号
        数值测试
            -gt 是否大于        -ge 是否大于等于
            -eq 是否等于        -ne 是否不等
            -lt 是否小于        -le 是否小于等于 
            ==  是否等于        !=  是否不等
            >   是否大于        <   是否小于
            =~  左侧是否包含右侧
            -z "string" 字符串是否为空,空则真
            -n "string" 字符串是否不空,不空则真
            字符串比较,操作数都用引号
                ["a" \> "c"] 1
                ["d" \> "c"] 0
                x=abcd;[[ "$x" =~ "ab" ]] && echo yes ||echo no  注意[[ ]]内有空格
                [[ "$num" =~ ^[0-9]+$ ]] && echo right ||echo wrong  是否为整数
                [[ "$num" =~ ^-?[0-9]+$ ]] && echo right ||echo wrong  是否为正整数/负整数
                [ -z "$var" ] && echo true || echo not true 
                [ -v var ] && echo true || echo not true  字符串是否被设置,被设置,则为真                      
        文件测试    判断顺序:软连接——>其他类型,若是软连接,则判断其对应的文件本身,而不是软连接
            -a/e file   是否存在,存在未真 
            -b file     是否存在,且为块设备
            -c file     是否存在,且为字符设备
            -d file     是否存在,且为目录
            -f file     是否存在,且为普通文件
            -p file     是否存在,且为命名管道
        大写-S file   是否存在,且为套接字
            -h/L file   是否存在,且为符号链接文件
            -r file     是否存在,且为可读
            -w file     是否存在,且为可写
            -x file     是否存在,且为可执行
            -u file     是否存在,且拥有suid权限
            -g file     是否存在,且拥有sgid权限
            -k file     是否存在,且拥有sticky权限
            -s file     是否存在,且非空
            -t fd       文件描述符是否已经打开
            -N file     从上一次读取,是否被修改
            -O file     当前用户是否为文件属主
            -G file     当前用户是否为文件属组
            file1 -ef file2 || echo $?  两个文件是否指向同一个设备上的inode
            file1 -nt file2 || echo $?  file1是否新于file2
            file1 -ot file2 || echo $?  file1是否旧于file2
            cmd1 && cmd2    expression1 -a expression2
            cmd1 || cmd2    expression1 -o expression2
            !cmd            !expression
                [ -L "/bin" ] && echo "软连接"
                [ !r $1 -a !w $1 ] && echo "不可读写" || echo "可读写"
                [ -f $1 -a "$1" =~ ".sh$" ] && chmod +x $1 && echo "有权限" || echo "无权限"
                [ -e $1 ] && echo "你可以登录" || (rm -rf /home/`whoami`;echo "你不能登录")  允许登录
                    [ -e $1 ] && (rm -rf /home/`whoami`;echo "你不能登录") || echo "你可以登录"  禁止登录
                    [ -f /etc/nologin ] && (echo "普通用户不能登录") || (touch /etc/nologin)
                    /etc/nologin存在,普通用户不能登录,删除它,普通用户可以登录  /run/nologin与/etc/nologin具有相同的效果
                判断主机是否为空,或等于主机名,如果是,则改为
                    [ -z "$HOSTNAME" -o "$HOSTNAME"==\"localhost.localdomain" ] && hostname www.fgq.com
                判断文件是否有执行权限
                    判断文件是否存在,判断文件是否有x权限
                写脚本,别名无效,不能在脚本中用
    练习
        参数小于1,提示用户usage:createuser.sh username,并退回脚本,返回状态码100,
        判断用户是否存在,若存在,提示用户已经存在,若不存在,创建用户,并提示创建成功
        vim createuser.sh 
            #!/bin/bash
            [ $# -lt 1 ] && echo "usage:createuser.sh username" && exit 100
            id $1 &> /dev/null && echo "$1已经存在" || (useradd $1 && echo $1创建成功)
        vim hostping.sh 
            #!/bin/bash
            ping -c1 -w1 $1 &> /dev/null && [[ $? -eq 0 ]] && echo "可以访问" || echo "不可以访问"
        vim checkdisk.sh 
            #!/bin/bash
            disk=$(df |tr -s " " |cut -d " " -f5 |cut -d % -f1 |sort -n |tail -1)
            inode=$(df -i|tr -s " " |cut -d " " -f5 |cut -d % -f1 |sort -n |tail -1)
            [ $disk -ge 80 ] && (echo "80%"|mail -s dislover80% root) || echo "磁盘空间足够"
            [ $inode -ge 80 ] && (echo "80%"|mail -s inodeover80% root) || echo "节点数足够"
        vim note.sh
            #!/bin/bash 
            true && (echo true;exist)  注意此处的exist退出,只是退出子程序,并未退出脚本,立即退出脚本:{echo true;exist;}函数(有点错误)
            echo not exist 
            
        read 把输入的值分配给一个和多个变量
            -p  指定显示的提示 
            -s  不显示输入的信息
            -n N        指定输入的字符长度
            -d '字符'     输入结束符
            -t N        几秒退出
            练习
                read name sex age
                        zhao mail 18
                    echo $name:zhao 
                read x y z <<< aa bb cc dd 
                    echo $z:cc dd 
                vim read.sh 
                    #!/bin/bash
                    echo -n "who do you like" 或者 echo -e "who do you like\c"
                    read name
                    echo "you like $name"
                vim read1.sh 
                    #!/bin/bash
                    echo -e "请输入密码:"
                    stty -echo  #不显示输入内容,但可执行此命令
                    read password
                    stty echo   #显示输入内容,执行此命令
                    echo "你的密码是$password"
                read -s -p "who do you like?" name  静默模式
                read -d a example  类似于多行重定向
                    fff
                    dfff
                    fa  a独立/不独立都可以
                   echo $example                    
                read命令不支持管道(|)
配置用户的环境
        配置文件
            都在/etc下
            全局生效
                /etc/profile  不建议更改
                /etc/profile.d/*.sh  可更改
                /etc/bashrc  不建议更改
            个人配置
                ~/.bash_profile  用户家目录下
                ~/.bashrc
            交互式登录 
                profile类
                    定义环境变量 运行命令和脚本
                终端:用户名+密码
                切换:su - 用户名
                顺序:/etc/profile ——> /etc/profile.d/*.sh ——> ~/.bash_profile ——> ~/.bashrc ——> /etc/bashrc
            非交互式登录 
                bashrc类
                    定义命令别名和函数
                    定义本地变量
                顺序:~/.bashrc ——> /etc/bashrc ——> /etc/profile.d/*.sh
            修改配置文件后,生效
                .或source ~/.bashrc
            bash退出任务
                保存在~/.bash_logout文件中
                在退出登录shell时运行
                自动备份 清除临时文件
                vim ~/.bash_logout
                    #~/.bash_logout
                    rm -rf /app/*
                    退出登录时,可自动删除/app下的文件内容
                    黑客删除登录执行cmd信息
            同一个变量配置,在多个文件中赋值,最后生效的是最后一次赋值(最后一个文件)
            定于对所有用户都生效的别名:/etc/bashrc
            所有用户登录,都发出提醒信息:/etc/profile/*.sh,建一个以.sh结尾的文件
            在原有的基础上,新增加子串
                president=obama
                president="tony $obama"
                echo $president
            echo $PATH
                export PATH="$PATH:/usr/local/apache/bin"  路径放在后面
                declare -x PATH="/usr/local/apache/bin:$PATH"  路径放在前面,优先找到
                echo $PATH  发现路径增加
                个人生效
                    vim ~/.bash_profile
                    export PATH=$PATH:/usr/local/apache/bin
                    . ~/.bash_profile
                全局配置
                    vim /etc/profile.d/apache.sh 
                    export PATH=$PATH:/usr/local/apache/bin
                    . ~/.bash_profile
            $-
                himBH
                    h   hash缓存
                    i   交互式 
                    m   
                    B   {}展开
                    H   history
                set 
                    +/-h    +增加功能  -去除功能
                    +/-i
                    +/-m 
                    +/-B
                    +/-H 
                $_ 缓存,相当于hash,一种功能
        面试题
            当前进程1283
            n=100;echo $$;(echo $$;echo $n;n=200;echo $n;echo $$);echo $n  
                      1283     1283    100           200     1283      100(开启子进程,执行完成后即退出,所以不是200)
            vim /etc/profile.d/test.sh 
                ^[[31m hello,dangerous!^[[0m
                ^[ 用ctrl+v打出来      ^[ 用ctrl+v打出来的  显示的颜色不同,按下ctrl+v后,出现^,在按向右的箭头即可
            用户环境的初始化脚本
                需要后期的积累
        

你可能感兴趣的:(shell脚本基础)