zoukankan      html  css  js  c++  java
  • 初识shell编程

    一、shell编程基础

    什么是shell

    命令解释器:你输入的命令,谁来给你运行、解释

    Centos默认的Shell是bash

    [root@luffy-001 log]# echo $SHELL    SHELL变量
    /bin/bash
    [root@luffy-001 log]# cat /etc/shells   所有的命令解释器
    /bin/sh
    /bin/bash
    /sbin/nologin
    /bin/dash
    /bin/tcsh
    /bin/csh
    [root@luffy-001 log]# cat /etc/passwd
    root:x:0:0:root:/root:/bin/bash      root用户 的命令解释器
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    sync:x:5:0:sync:/sbin:/bin/sync
    shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
    halt:x:7:0:halt:/sbin:/sbin/halt
    mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
    uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
    operator:x:11:0:operator:/root:/sbin/nologin
    games:x:12:100:games:/usr/games:/sbin/nologin
    gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
    ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
    nobody:x:99:99:Nobody:/:/sbin/nologin
    dbus:x:81:81:System message bus:/:/sbin/nologin
    vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
    abrt:x:173:173::/etc/abrt:/sbin/nologin
    haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
    ntp:x:38:38::/etc/ntp:/sbin/nologin
    saslauth:x:499:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin
    postfix:x:89:89::/var/spool/postfix:/sbin/nologin
    sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
    tcpdump:x:72:72::/:/sbin/nologin
    pizza:x:500:500::/home/pizza:/bin/bash
    oldboy:x:501:501::/home/oldboy:/bin/bash

    什么是Shell脚本

    命令大礼包,就是一个程序文件,包含若干行Linux命令语句

    循环,条件语句

    一般以.sh结尾,或者通过file命令查看,其类型是shell script

    [root@luffy-001 log]# file /server/scripts/ip.sh 
    /server/scripts/ip.sh: Bourne-Again shell script text executable

    创建Shell脚本

    1、统一脚本存放目录,/server/scripts/

    2、推荐使用vim编辑器编辑脚本,脚本以.sh结尾

    3、第一行支出是由哪一个解释器来执行脚本中的内容

    #!/bin/bash
    
    /sbin/ifconfig eth0|awk -F '[ :]+' 'NR==2{print $4}'

    #! 称为幻数,能被内核识别

    必须写在第一行,如果不是第一行就编程脚本注释行

    又可以写成#!/bin/sh

    4、版权声明

    # desc:     show  ip address
    # author:   pizza
    # time:     20191111
    # version:  v1.0

    5、题目:写一个简单的脚本(切换目录显示文件实行)并运行

    二、shell脚本深入

    变量

    用一个固定的字符串,替代更多更复杂的内容,省事

    x=1
    [root@luffy-001 scripts]# x=1
    [root@luffy-001 scripts]# echo $x
    1
    devPath=/server/ filelist=`ls`或者$(ls)
    $LANG
    $PATH

    变量又分为局部变量(普通变量)、全局变量(环境变量)、特殊变量

    局部变量(普通变量)

    只能在创建他们Shell函数或者Shell脚本中使用

    形式:变量名=value
    
    变量名要求:
    #1、字母、数字、下户线组成
    #2、必须以字母开头
    #3、见名知意,规范变量名写法定义
    #4、驼峰写法:RedPizzaCar
    
    应用变量尽量使用${}
    [root@luffy-001 scripts]# week=10
    [root@luffy-001 scripts]# echo ${week}day
    10day
    [root@luffy-001 scripts]# echo $weekday
    #这样写什么也没有
    [root@luffy-001 scripts]# 
    
    可以把一个命令作为变量
    普通字符串via你来定义测试

    全局变量(环境变量)

    大写、linux里面哪里都可以用(常用的$PATH、$LANG、$PS1)

    在创建他们的Shell及其派生出来的子Shell中使用

    与普通变量的区别

    注意:普通变量是无法直接调用的,需要使用export将其转化成全局变量后方能使用,例子见下面代码

    [root@luffy-001 scripts]# vim pizza.sh
    #!/bin/bash
    
    echo $PIZZA
    
    ~                                                                                                      
    ~                                                                                                      
    ~                                                                                                      
                                                                                                         
    "pizza.sh" 4L, 26C written
    [root@luffy-001 scripts]# PIZZA=10
    [root@luffy-001 scripts]# echo $PIZZA
    10
    [root@luffy-001 scripts]# sh pizza.sh 
    
    
    [root@luffy-001 scripts]# export PIZZA
    [root@luffy-001 scripts]# sh pizza.sh 
    10
    [root@luffy-001 scripts]# 

    分类

    1、Shell通过环境变量来确定登录用户名、命令路径、终端类型、登录目录(LANG、PATH、SHELL、UID)

    2、自定义环境变量:

    建议所有环境变量均为大写

    必须用export命令定义

    查看全局变量:env

    取消全局变量:unset  PIZZA

    与用户环境变量有关的文件目录

    全局环境变量配置文件

    /etc/profile  

    /etc/bashrc

    /etc/profile.d/    用户登录到系统,会运行这个目录下面的脚本(脚本要有执行权限chmod +x)

    用户环境变量配置文件

    ~/.bash_profile

    ~/.bashrc

    特殊变量

    位置变量

    $0  获取当前执行脚本的文件名。如果执行脚本带路径,那么就包括脚本路径。模拟系统脚本使用$0

    $n  n代表脚本后面的参数,

    $#  脚本一共有多少个参数,参数的个数

    进程状态变量

    $? 显示上一个命令的执行结果

    ###命令执行正确  结果0

    ###命令执行错误  结果非0

    变量赋值-如何向变量中放内容

    1、直接给!

    x=1
    y=2
    echo $x $y
    
    w=$1
    s=$2
    echo $w $s
    
    [root@luffy-001 scripts]# sh pizza.sh  10 20 
    10
    1 2
    10 20

    2、read

    [root@luffy-001 scripts]# read -p "input x y:" x y
    input x y:10 20
    [root@luffy-001 scripts]# echo $x $y
    10 20

    可以将该语句直接放入脚本中执行

    read得到的输入默认存储在变量REPLY中

    可以存储在一个或者多个变量中

    -p  “提示语句”, 屏幕就会显示交互式语句

    -t   等待时间

    -s  关闭回显,用于密码输入

    条件表达式

    [ <测试表达式> ]注意两边要有空格

    判断文件

    -f 判断文件是否存在
    [root@luffy-001 scripts]# [ -f /server/scripts/pizza.sh ] [root@luffy-001 scripts]# echo $? 0
    -d 判断目录是否存在 [root@luffy
    -001 scripts]# [ -d /server/scripts/ ] [root@luffy-001 scripts]# echo $? 0

    判断整数

    等于equal                              -eq     

    不等于not equal                     -ne

    大于 greater than                   -gt

    大于等于 greater equal           -ge

    小于less than                          -lt

    小于等于 less equal                -le

    [root@luffy-001 scripts]# [ 1 -eq 1 ]
    [root@luffy-001 scripts]# echo $?
    0

    简单案例:判断命令行参数个数等于2(配合&&)

    1
    [ $# -eq 2 ]  && echo "arg:"$#

    题目:如果/oldboy目录不存在则创建

    [ -d /oldboy ] || mkdir -p /oldboy


    题目:如果/root/oldboy.txt存在则提示文件已存在

    [ -f /root/oldboy.txt ] && echo file esists

    if条件语句

    单分支条件语句

    语法:if [条件];then 命令 fi

    题目:如果第一个数比第二个数大,显示第一个数大于第而个数

    num1=$1
    num2=$2
    
    if [ $num1 -gt $num2 ]; then
        echo $num1 bigger than $num2
    fi

    多分支条件语句

    语法:if [条件];then 命令 else 命令 fi

    num1=$1
    num2=$2
    
    if [ $num1 -gt $num2 ]; then
        echo $num1 bigger than $num2
    elif [ $num1 -lt $num2 ]; then 
      echo $num1 less than $num2
    else
        echo $num1 equal $num2
    fi

    现在有一个问题是这样的:如果参数过多,也会运行,后面多余的参数变成了无用参数,只有一个参数会报错

    num1=$1
    num2=$2
    
    if [ $# -ne 2 ];then
        echo "Usage:"please  input 2 num : num1 num2
        exit   
    fi
    
    if [ $num1 -gt $num2 ]; then
        echo $num1 bigger than $num2
    else
        echo $num1 less than $num2
    fi

    [ -d /oldboy ]   相当于 test -d /oldboy

    man test

    for循环语句

    语法格式:

    for  变量名字  in  列表
    do
      命令
    done

    例子:

    [root@luffy-001 scripts]# for num in 1 2 3 4 5 
    > do
    > echo "the num is:$num"
    > done
    the num is:1
    the num is:2
    the num is:3
    the num is:4
    the num is:5

    生成序列seq 或者{}

    [root@luffy-001 scripts]# for num in {1..10}; do echo "the num is:$num"; done
    the num is:1
    the num is:2
    the num is:3
    the num is:4
    the num is:5
    the num is:6
    the num is:7
    the num is:8
    the num is:9
    the num is:10

    题目:优化linux开机启动项,只保留crond;sshd;network;rsyslog;sysstat,其他的都关闭

    提示:chkconfig 服务  off

    for name  in  $(chkconfig |egrep  "crond|sshd|rsyslog|network|sysstat" -v |awk '{print $1}')
    do
         chkconfig $name off
    done
    View Code

    查看脚本执行过程 -x

    三、Shell脚本初步入门

    什么是是shell?

    shell原意,是贝壳,

    它在Linux内核和应用程序之间。就像一个翻译官,中间人,让程序员员写的应用程序能和系统之间相互交流

    什么是shell脚本?

    简单点说,就是许多shell命令,写在一个文件中,实现一个需求

    先看一个清除日志的脚本

    # clean logs ,v0.1
    cd /var/log
    cat /dev/null>message
    echo "Logs clean up"

    这个脚本的问题在于,三条命令之间没有表现明确的关系

    我们不知道脚本执行后,会是什么样的结果!

    下面看一个完善的版本

    #! /bin/bash
    LOG_DIR=/var/log
    ROOT_UID=0
    
    # 第一关,必须是root用户才能执行脚本,否则提示,并终止
    if ['$UID' -ne '$ROOT_UID']
    then
            echo 'Must be root to run this script!'
            exit 1
    fi
    # 第二关,成功切换到目录,否则提示,并终止
    cd $LOG_DIR || {
            echo 'Can not change to necessary directory!'
            exit 1
    }
    # 第三关,清理日志,如果清理成功,给出正确的提示
    cat /dev/null>message && {
            echo 'Logs cleaned up!'
            exit 0
    }
    
    # 第四关,通关失败,给出提示
    echo 'cleaned logs fail!'
    exit 1

    shell脚本在Linux中的地位

    Shell脚本语言很擅长处理纯文本类型的数据,而Linux系统中几乎所有的配置文件、日志文件(如NFSRsyncHttpdNginxLVSMySQL等),以及绝大多数的启动文件都是纯文本类型的文件。

    自然学好Shell脚本语言,就可以利用它在Linux系统中发挥巨大的作用。

    形象的将shell和各种运维工具比喻为一串项链以及三种SHELL语言分类

    Shell脚本的建立

    1、脚本的第一行

    一个规范的Shell脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在Linux bash编程中一般为:
    #!/bin/bash#!/bin/sh #<==255个字符以内

    2、bash和sh的区别

    早期的bashsh稍有不同,它还包含了cshksh的特色,但大多数脚本都可以不加修改地在sh上运行。

    3、需要注意的地方

    CentOSRed Hat Linux下默认的Shell均为bash。因此,在写Shell脚本的时候,脚本的开头即使不加#!/bin/bash,它也会交给bash解释。

    如果写脚本不希望使用系统默认的Shell解释,那么就必须要指定解释器了。否则脚本文件执行的结果可能就不是你想要的。

    建议读者养成好的编程习惯,不管什么脚本最好都加上相应的开头解释器语言标识,养成Shell编程规范。

    Shell脚本的执行

    1. bash script-namesh script-name

    这是当脚本文件本身没有可执行权限(即文件权限属性x位为-号)时常使用的方法,或者脚本文件开头没有指定解释器时需要使用的方法,这也是老男孩老师推荐的使用方法

    2. path/script-name./script-name

    指在当前路径下执行脚本(脚本要有执行权限),需要先将脚本文件的权限改为可执行(即文件权限属性加x位),

    具体方法为chmod +x script-name。然后通过脚本绝对路径或相对路径就可以直接执行脚本了。

    3. source script-name. script-name

    关于点和souece的妙用,就是父子shell之间可以相互调用变量

    [root@dao scripts]# sh sh_user.sh   # 文件中是user=‘whoami’ 相当于定义了局部变量
    [root@dao scripts]# echo $user
    
    [root@dao scripts]# . ./sh_user.sh  # 使用点,调用了子变量(局部变量)
    [root@dao scripts]# echo $user
    whoami

    4. sh<script-namecat scripts-name|sh

    [root@dao scripts]# chkconfig --list |grep 3:on |awk '{print "chkconfig",$1,"off"}'|bash

    shell脚本执行一个重要的例子

    当我们登陆命令行,就相当于开启一个shell,也就是局部变量和全局变量的意思,只能在创建他们的shell函数或者shell脚本中使用。

    https://www.cnblogs.com/yxiaodao/p/10401327.html#_label2

    取消定义变量unset user

    shell中批量注释

    1、vim批量操作

    2、:EOF

    EOF (这个EOF前面不能有空格)原理就是,EOF把中间的内容交给冒号,但是冒号表示什么都不做,所以,也就是注释了

    3、cat > /dev/null <<EOF

    EOF

    shell脚本的执行过程

    shell脚本的编程规范和习惯

    1.开头加脚本解释器
    2.附带作者及版权信息
    3.脚本扩展名为*.sh
    4.脚本存放在固定的目录下
    5.脚本中不用中文
    6.成对的符号一次书写完成
    7.循环格式一次性输入完成

    Shell变量核心基础知识与实践

    什么是变量?

    x=1,x就是变量名,=号表示赋值。用一个字符或者字符串表示一堆内容。这个字符或者字符串叫做变量

    变量的特性?

    在bash shell中不会区分变量的类型

    变量的分类?

    两类,环境变量(全局变量)和普通变量(局部变量)。https://www.cnblogs.com/yxiaodao/p/10401327.html#_label2

    Shell环境变量

    环境变量(全局变量),系统中默认就存在的,作用就是解决系统的一些问题。

    显示环境变量的方法:

    1、echo $变量名

    2、env

    3、set     set -o 查看bash编程的配置

    定义环境变量:

    PS1、PATH、HOME、UID 系统固有的,默认就表示一定意义。

    3种定义环境变量的方法(环境变量尽量大写)

    1、直接export

    export  PIZZA=1

    2、先赋值,在export

    PIZZA=2

    export  PIZZA

    3、declare

    -x  A=1

    -i  表示定义整型

    环境变量永久生效的配置/etc/profile

    环境变量取消 unset  PIZZA

    环境变量的文件执行顺序

    全局文件

    /etc/profile

    /etc/bashrc/

    用户环境变量文件

    ~/.bashrc

    ~/.bash_profile

    上图是文件加载的顺序

    注意:新添加的环境变量要加在文件的前面,不要加在最后面,其中的代码会影响执行顺序。

    注意:在ssh 远程登录或者其他特殊情况下,也许不会加载/etc/profile和~/.bash_profile

    所以在添加变量的时候,添加在/etc/bashrc下

    Shell普通变量

    局部变量

    当前用户或者脚本中生效

    a.变量名:
    规则:字母、数字、下划线,3者组合,以字母开头。
    要求:见名知意1)OldboyAge=1
    2)oldboy_age=1
    3) oldboyAge=1  ###驼峰语法
    
    b.变量内容
    字符串:
    变量名=value     #<==不加引号。 ##解析变量或者命令,然后输出,纯数字选择不加引号。
    变量名=’value’   #<==加单引号。 ###所见即所得
    变量名=”value”   #<==加双引号。 ##解析变量或者命令,然后输出,字符串默认选择双引号,可以把要定义的内容作为一个整体。
    
    命令变量,把一个命令的结果赋值给变量
    变量名=`ls`
    变量名=$(ls)

    小结:
    针对变量名:
      1、变量名一定要有一定的命令规范,并且要见名知意,推荐使用驼峰法
      2、变量名仅能使用字母、数字、下划线的任意多个字符,并且要字母开头
    针对变量内容:
      3、在脚本中定义普通字符变量,尽量使用双引号括起来。
      4、单纯数字的变量内容可以不加引号
      5、希望变量的内容鸳鸯输出需加单引号
      6、希望变量值引用命令并获取命令的结果就用反引号或者$()
    针对赋值符号:
      7、变量定义使用赋值等号(=),赋值符号两端不要有空格
    针对变量输出:
      8、使用或者输出变量的内容,可用 $ 变量名,$PS1
      9、若变量名(db)后面有其他字符链接的时候,就必需给变量名加上大括号

    Shell特殊位置参数变量

    Shell中存在一些特殊且重要的变量,例如: $0$1$#,我们称之为特殊位置参数变量,

    要从命令行、函数、或脚本执行等处传递参数时,就需要在Shell脚本中使用位置参数变量。

    特殊位置变量:
    $0  获取脚本的名字,如果脚本前面跟着路径的话,那就获取路径加上脚本名字。
    [root@web01 scripts]# cat test.sh
    #!/bin/bash
    echo $0
    [root@web01 scripts]# bash test.sh 
    test.sh
    [root@web01 scripts]# bash /server/scripts/test.sh 
    /server/scripts/test.sh
    企业应用;
    一般在启动脚本的结尾会使用$0获取脚本的路径和名字给用户提示用。
    /etc/init.d/crond
    
    $1,$2----$n
    $1表示脚本后的第一个参数
    $2表示脚本后的第二个参数
    ....
    超过$9,${10}  要加{}大括号
    企业应用:
    case "$1" in
        start)
            rh_status_q && exit 0
            $1
            ;;
        stop)
    
    $# 脚本后面所有参数的个数
    企业应用:
    [root@web01 scripts]# cat test.sh 
    #!/bin/bash
    if [ $# -ne 2 ]
    then
       echo "Usage:$0 arg1 arg2"
       exit 1
    fi
    echo ok
    
    
    $*  获取脚本的所有参数,“$1 $2 $3”
    $@  获取脚本的所有参数,"$1" "$2" "$3"
    
    当需要接收脚本后面所有参数时,但是又不知道参数个数就用这两个变量。
    
    区别:
    [root@web01 scripts]# cat test.sh 
    #!/bin/bash
    for arg in "$*"
    do
      echo $arg
    done
    
    echo --------------------------
    
    for arg1 in "$@"
    do
      echo $arg1
    done
    [root@web01 scripts]# bash test.sh "I am" oldboy teacher.
    I am oldboy teacher.
    --------------------------
    I am
    oldboy
    teacher.
    
    make
    echo $?

    Shell进程特殊状态变量

    $? 获取上一个命令的返回值,如果返回值为0就证明上一个命令执行正确,
    非0,就证明上一个命令执行失败的。
    *****
    
    $$ 获取当前执行脚本的进程号
    
    $! 获取上一个后台工作进程的进程号
    
    $_ 获取上一个执行脚本的最后一个参数

    Shell变量子串

    Shell变量子串(变量内容相关知识)
    
    [root@web01 scripts]# oldboy="I am oldboy"
    [root@web01 scripts]# echo ${oldboy}
    I am oldboy
    [root@web01 scripts]# echo ${#oldboy}
    11
    [root@web01 scripts]# echo $oldboy|wc -L
    11
    [root@web01 scripts]# expr length "$oldboy"
    11
    [root@web01 scripts]# echo $oldboy|awk '{print length}'
    11
    [root@web01 scripts]# echo $oldboy|awk '{print length ($1)}'
    1
    [root@web01 scripts]# echo $oldboy|awk '{print length ($0)}'
    11
    
    
    练习题:
    I am oldboy I teach linux
    打印这些字符串中字符数小于3的单词。
    涉及知识点:取字符串长度,for,if。
    
    [root@web01 scripts]# echo ${oldboy:2}
    am oldboy
    [root@web01 scripts]# echo ${oldboy:2:2}
    am
    [root@web01 scripts]# echo ${oldboy:2:4}
    am o
    OLDBOY=abcABC123ABCabc
    
    
    [root@web01 scripts]# echo ${OLDBOY}
    abcABC123ABCabc
    [root@web01 scripts]# echo ${OLDBOY%a*C}
    abcABC123ABCabc
    [root@web01 scripts]# echo ${OLDBOY%a*c}
    abcABC123ABC
    [root@web01 scripts]# echo ${OLDBOY%%a*c}
    
    [root@web01 scripts]# 

    Shell特殊变量扩展知识

    只挑了4个,最重要的是第1个

    [root@web-01 ~]# 
    [root@web-01 ~]# result=${test:-UNSET}
    [root@web-01 ~]# echo $test
    
    [root@web-01 ~]# echo $result
    UNSET
    [root@web-01 ~]# echo $test
    
    [root@web-01 ~]# #当变量test为空的时候,就把UNSET内容赋值给result

    result 媳妇
    test 销售 空
    UNSET 备胎 赋值给result

    企业用途
    find $path -name "*.name" -mtime +7|xrangs rm -f
    当 $path 为空的时候,命令会从根目录开始删,所以我们准备一个备胎
    find  ${path:-/tmp}  -name  "*.name"  -mtime +7|xrangs  rm  -f

    系统中的应用---httpd的启动脚本中

    - = ? +

    Shell变量的数值计算

    算数运算符

    如果要执行算术运算,就离不开各种运算符号,和其他编程语言类似, Shell也有很多算术运算符,下面就给大家介绍下常见的Shell算术运算符

    Bash编程常见运算命令汇总

    只适合整数运算
    1、(()) 推荐
    2、let 次推荐
    3expr
    4、$[]
    既适合整数,又适合小数运算。
    1、bc
    2awk 推荐

    (())
    [root@web-01 ~]# a=1
    [root@web-01 ~]# b=2
    [root@web-01 ~]# echo $a+1   不加
    1+1
    [root@web-01 ~]# $(a+1) 更不加了
    -bash: a+1: command not found
    [root@web-01 ~]# echo $(a+1)
    -bash: a+1: command not found

    [root@web-01 ~]# echo $((a+1)) 双括号才加
    2
    [root@web01 scripts]# echo $((2**3))
    8
    [root@web01 scripts]# echo $((1+2**3-5/3))
    8
    [root@web01 scripts]# echo $((1+2**3-5%3))
    7
    问? ++a 和 a++有什么区别

    let
    [root@web-01 ~]# a=1
    [root@web-01 ~]# b=2
    [root@web-01 ~]# i=$a+$b
    [root@web-01 ~]# echo $i
    1+2
    [root@web-01 ~]# let i=$a+$b
    [root@web-01 ~]# echo $i
    3

    expr用于运算,必须要有空格
    [root@web-01 ~]# expr 1 + 1
    2
    [root@web-01 ~]# expr 1+1
    1+1

    $[]
    [root@web-01 ~]# echo $[2+2]
    4
    [root@web-01 ~]# echo $[2 + 2]
    4

    ===========
    bc 就像启动计算器,高端点用法自己在了解一下
    [root@web-01 ~]# bc
    bc 1.06.95
    Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
    This is free software with ABSOLUTELY NO WARRANTY.
    For details type `warranty'.
    1+1
    2
    3+4
    7
    1
    1
    1*1.2
    1.2

    [root@web-01 /server/scripts]# echo "scale=2;355/113"|bc
    3.14 使用scale来保留2位小数
    [root@web-01 /server/scripts]# echo "scale=3;355/113"|bc
    3.141

    awk的运算效果很好!运算精确,好用
    [root@web-01 ~]# echo 1.1 0.5 |awk '{print $1-$2}'
    0.6

    expr命令的多种企业应用实践

    1、判断变量的类型是否为整数

    在shell编程里,由于函数库很少,因此在判断字符串是否为整数时,就不是很容易的事情

    可以利用expr做计算时必须是整数的规则,把一个变量或者字符串和一个一直西横竖(非0)相加,

    看命了返回值是否为0。就认为做加法的变量为整数,否则就不是整数。

    [root@web-01 ~]# i=pizza         将字符串赋值给i                 
    [root@web-01 ~]# expr $i + 6 &>/dev/null   把i和6相加,&>/dev/null表示不保留任何输出
    [root@web-01 ~]# echo $?   输出返回值
    2    非0 就证明输出了错误,证明1不是整数
    [root@web-01 ~]# i=4 [root@web-01 ~]# expr $i + 6 &>/dev/null [root@web-01 ~]# echo $? 0 返回为0,则证明i的值是整数

    编写一个脚本来判断变量是不是整数

    [root@web-01 /server/scripts]# vim judge1.sh
    #!/bin/bash
    
    echo 6 + $1 &>/dev/null
    if [ $? -eq 0 ]
    then
            echo "$1 是整数"
    else
            echo "$1 不是整数"
    fi
    
    "judge1.sh" [New] 9L, 112C written  
    [root@web-01 /server/scripts]# bash judge1.sh 123
    123 是整数
    [root@web-01 /server/scripts]# bash judge1.sh ll
    ll 是整数

    2、判断文件扩展名是否符合要求

    [root@web-01 /server/scripts]# vim judge2.sh
      1 #!/bin/bash
      2 # :冒号两边要有空格
      3 expr "$1" : ".*.txt" &>/dev/null
      4 if [ $? -eq 0 ]
      5 
      6 then
      7         echo "$1 是文本"
      8 else
      9         echo "$1 不是文本"
     10 fi
    ~                                                                                       
    ~                                                                                                                                                                  
    ~                                                                                       
    "judge2.sh" 10L, 152C written                                         
    [root@web-01 /server/scripts]# bash judge2.sh old
    old 不是文本
    [root@web-01 /server/scripts]# bash judge2.sh old.txt
    old.txt 是文本

    expr 好有很多功能,请自行了解(man expr)

    bash内置核心命令

      Shell变量除了可以 直接赋值 或者 脚本传参 外!还可以使用read命令从标准输入中获得,

      read为bash内置命令,可以通过help read查看帮助。

      语法格式:read [参数] [参数]

      常用参数如下。

        -p prompt:设置提示信息

        -t  timeout:设置输入等待时间,单位默认为秒。

    [root@web-01 /server/scripts]# read -p "请输入一个数字:" a
    请输入一个数字:11
    [root@web-01 /server/scripts]# echo $a
    11

    read一般的作用就是和用户交互

    [root@web-01 /server/scripts]# read -p "请输入两个数字:" a b
    请输入两个数字:12 56
    
    [root@web-01 /server/scripts]# echo $((a+b))
    68

    bash内置核心命令read的企业级应用实践

    打印一个菜单,让用户选择要做的事情

    #!/bin/bash
    cat <<EOF
            1install lamp
            2install lnmp
            3、exit
    EOF
    read -p "请选择一个序号(必须是一个数字):" num
    # 先判断一下是不是数字
    expr 2 + $num &>/dev/null if [ $? -ne 0 ] then echo "usage:$0 {1|2|3}" exit 1 fi if [ $num -eq 1 ] then echo "install lamp..." elif [ $num -eq 2 ] then echo "install lamp..." elif [ $num -eq 3 ] then echo "bye" else echo "usage:$0 {1|2|3}" exit 1 fi
    数值运算命令:
    只适合整数运算
    1、(()) 推荐
    2、let 次推荐
    3expr
    4、$[]
    既适合整数,又适合小数运算。
    1、bc
    2awk 推荐
    
    1、(()) 推荐
    [root@web01 scripts]# i=$((a+1))
    [root@web01 scripts]# echo $i
    2
    [root@web01 scripts]# 
    [root@web01 scripts]# echo $((a+3))
    4
    [root@web01 scripts]# echo $((1+3))
    4
    [root@web01 scripts]# echo $((2**3))
    8
    [root@web01 scripts]# echo $((1+2**3-5/3))
    8
    [root@web01 scripts]# echo $((1+2**3-5%3))
    7
    
    2、let 次推荐
    [root@web01 scripts]# let i=$a+1
    [root@web01 scripts]# echo $i
    2
    
    3、expr用于运算
    
    4、$[]
    
    =============
    bc
    awk
    [root@web01 scripts]# echo 1+2|bc
    3
    [root@web01 scripts]# 
    [root@web01 scripts]# echo 1.1+2|bc
    3.1
    [root@web01 scripts]# echo 1.1+2.3|bc
    3.4
    [root@web01 scripts]# echo 2.1 1.4|awk '{print $1-$2}'
    0.7
    [root@web01 scripts]# echo 2.1 1.4|awk '{print $1*$2}'
    2.94
    
    
    [root@web01 scripts]# expr 2 + 2
    4
    [root@web01 scripts]# expr 2 + a
    expr: non-numeric argument
    [root@web01 scripts]# echo $?
    2
    [root@web01 scripts]# 
    [root@web01 scripts]# 
    [root@web01 scripts]# 
    [root@web01 scripts]# a=2
    [root@web01 scripts]# expr 2 + $a &>/dev/null
    [root@web01 scripts]# echo $?
    0
    [root@web01 scripts]# a=oldboy
    [root@web01 scripts]# expr 2 + $a &>/dev/null
    [root@web01 scripts]# echo $?
    2
    
    
    [root@web01 scripts]# cat judge1.sh
    #!/bin/bash
    expr 2 + $1 &>/dev/null
    if [ $? -eq 0 ]
    then
       echo "$1 is 整数"
    else
       echo "$1 不是整数"
    fi
    [root@web01 scripts]# bash judge1.sh 123
    123 is 整数
    [root@web01 scripts]# bash judge1.sh oldboy
    oldboy 不是整数
    
    
    root@web01 scripts]# cat judge_kuozhan.sh 
    #!/bin/bash
    expr "$1" : ".*.txt" &>/dev/null
    if [ $? -eq 0 ]
    then
        echo "$1 是文本"
    else
        echo "$1 不是文本"
    fi 
    [root@web01 scripts]# sh judge_kuozhan.sh oldboy.txt
    oldboy.txt 是文本
    [root@web01 scripts]# sh judge_kuozhan.sh alex.log
    alex.log 不是文本
    [root@web01 scripts]# sh judge_kuozhan.sh peiqi.log
    peiqi.log 不是文本
    [root@web01 scripts]# sh judge_kuozhan.sh 老男孩老师.txt
    老男孩老师.txt 是文本
    
    
    [root@oldboy scripts]# cat test.sh    
    #!/bin/bash
    a=6
    b=2
    echo "a-b=$(($a-$b))"
    echo "a+b=$(($a+$b))"
    echo "a*b=$(($a*$b))"
    echo "a/b=$(($a/$b))"
    echo "a**b=$(($a**$b))"
    echo "a%b=$(($a%$b))"
    
    变量的赋值:
    1、定义法
    a=1
    
    2、传参法
    [root@web01 scripts]# cat test7.sh 
    #!/bin/bash
    a=$1
    b=$2
    echo "a-b=$(($a-$b))"
    echo "a+b=$(($a+$b))"
    echo "a*b=$(($a*$b))"
    echo "a/b=$(($a/$b))"
    echo "a**b=$(($a**$b))"
    echo "a%b=$(($a%$b))"
    
    3、read读入,读取用户输入。
    -p 提示
    -t 等待用户输入的时间
    
    read -t 30 -p "请输入一个数字:"
    [root@web01 scripts]# read -t 30 -p "请输入一个数字:" a
    请输入一个数字:11
    [root@web01 scripts]# echo $a
    11
    [root@web01 scripts]# a=11
    [root@web01 scripts]# echo $a
    11
    
    read读入有什么作用
    和用户交互。
    
    [root@web01 scripts]# cat test6.sh
    #!/bin/bash
    read -p "请输入两个数字:" a b
    echo "a-b=$(($a-$b))"
    echo "a+b=$(($a+$b))"
    echo "a*b=$(($a*$b))"
    echo "a/b=$(($a/$b))"
    echo "a**b=$(($a**$b))"
    echo "a%b=$(($a%$b))"
    
    
    read企业应用
    [root@web01 scripts]# cat select1.sh
    #!/bin/bash
    cat <<EOF
      1.install lamp
      2.install lnmp
      3.exit
    EOF
    read -p "请选择一个序号(必须是数字):" num
    #1.判断是否为整数
    expr 2 + $num &>/dev/null
    if [ $? -ne 0 ]
    then
        echo "Usage:$0 {1|2|3}"
        exit 1
    fi
    
    #2.判断执行处理
    if [ $num -eq 1 ]
    then
        echo "install lamp..."
    elif [ $num -eq 2 ]
    then
        echo "install lnmp..."
    elif [ $num -eq 3 ]
    then
        echo "bye."
        exit 
    else
        echo "Usage:$0 {1|2|3}"
        exit 1
    fi

    Shell的常见条件表达式语法介绍和实践

    通常,在bash的各种条件结构和流程控制结构中都要进行各种测试,然后根据测试结果执行不同的操作,有时也会与if等条件语句相结合,来完成测试判断,减少程序运行的错误。执行测试条件表达式后通常会返回,就像执行命令后的返回值为0表示真,非0表示假一样。

    条件表达式常见语法

    条件表达式的编程语法

    [ 测试表达式 ]  && 命令1 | |  命令2

    如果前面的表达式成功,name就执行命令1,否则执行命令2

    ========相当于========

    if  [ 测试表达式 ]

    then

      命令1

    else

      命令2

    fi

    当命令很多的时候

    [ 测试表达式 ]  && {

      命令1

      命令2

    }| |{

      命令3

      命令4

    }

    如果前面的表达式成功,那么久执行后面命令

    [ 测试表达式 ]  && 命令1

    如果前面表达式失败,那么就执行后面的命令

    [ 测试表达式 ]  | | 命令2

    条件表达式
    [root@web-01 /server/scripts]# [ -e /etc/hosts ] && echo 1 || echo 0
    1
    [root@web-01 /server/scripts]# [ -e /etc/host ] && echo 1 || echo 0
    0
    命令表达式
    [root@web-01 /server/scripts]# (expr 1 + 1 &>/dev/null) && echo 1 || echo 0
    1
    [root@web-01 /server/scripts]# (expr 1 + a &>/dev/null) && echo 1 || echo 0
    0

    文件测试表达式的常见功能

    在man test 中可以找到这些内容

    字符串测试表达式常见功能说明

    注意:

    1、字符串就用双引号

    2、等号可以用一个或者两个

    3、等号两端必须要有空格

    整数测试表达式

    记住在单括号和双括号的写法的区别,测试一下

    逻辑测试表达式

    不同符号测试表达式 [ ]  [[ ]] (( ))  test 的区别

    
    条件表达式6种写法:if,while
    语法1: test <测试表达式>
    语法2: [ <测试表达式> ]    #两端有空格
    语法3:[[ <测试表达式> ]]  #两端有空格
    语法4:((<测试表达式>))    #不需要空格
    
    语法5:(命令表达式) 
    语法6:`命令表达式`
    
    编程语法:
    [ <测试表达式> ] && 命令1
    如果前面表达式成功,那么就执行后面命令。
    
    [ <测试表达式> ] || 命令1
    如果前面表达式失败,那么就执行后面命令。
    
    [ <测试表达式> ] && {
    命令1
    命令2
    命令3
    }
    如果前面表达式成功,那么就执行后面命令。
    
    [ <测试表达式> ] && 命令1 || 命令2
    如果前面表达式成功,那么就执行命令1,否则执行命令2。
    
    [ <测试表达式> ] && {
    命令1
    命令2
    }||{
    命令3
    命令4
    }
    如果前面表达式成功,那么就执行命令1,2,否则执行命令3,4<测试表达式>有哪些:
    
    
    文件测试表达式:
    为什么需要文件测试表达式?
    操作一个对象,就要看对象条件是否满足,否则不要操作。
    1、常见功能
    2、实践
    3、企业应用:启动脚本中的应用。
    
    
    字符串测试表达式
    [ -n "字符串" ]    字符串长度[不]为0,表达式为真。 not zero。
    [ -z "字符串" ]    字符串长度为0,表达式为真。 zero。
    [ "字符串1" == "字符串2" ]  两个字符串相同则为真。
    [ "字符串1" !== "字符串2" ] 两个字符串不相同则为真。
    
    
    注意:
    1字符串就用双引号
    2等号可以用一个或者两个3=号两端必须要有空格。
    实践:
    [root@web01 ~]# [ -n "oldboy" ] && echo 1 || echo 0
    1
    [root@web01 ~]# [ -z "oldboy" ] && echo 1 || echo 0
    0
    [root@web01 ~]# char="oldboy"
    [root@web01 ~]# [ -z "$char" ] && echo 1 || echo 0
    0
    [root@web01 ~]# unset char
    [root@web01 ~]# [ -z "$char" ] && echo 1 || echo 0
    1
    [root@web01 ~]# [ "dd" == "dd" ] && echo 1 || echo 0
    1
    [root@web01 ~]# [ "dd" == "ff" ] && echo 1 || echo 0
    0
    [root@web01 ~]# [ "dd" = "ff" ] && echo 1 || echo 0
    0
    [root@web01 ~]# [ "dd" != "ff" ] && echo 1 || echo 0
    1
    [root@web01 ~]# [ "dd" != "dd" ] && echo 1 || echo 0
    0
    
    改造上面的四则运算的脚本
    企业应用:
    [root@db03 scripts]# cat yunsuan1.sh
    #!/bin/bash
    ##############################################################
    # File Name: yunsuan.sh
    # Version: V1.0
    # Author: oldboy
    # Organization: www.oldboyedu.com
    # Created Time : 2018-05-30 09:03:10
    # Description:
    ##############################################################
    #!/bin/bash
    read -p "pls input two num:" a b
    
    # b没有值,肯定是没有输入或者没有输入2个
    if [ -z "$b" ] then echo "请输入两个数" exit 1 fi
    # 判断是不是数字
    expr $a + $b + 1 &>/dev/null if [ $? -ne 0 ] then echo "Usage:$0 num1 num2" exit 1 fi echo "a-b=$(($a-$b))" echo "a+b=$(($a+$b))" echo "a*b=$(($a*$b))" echo "a/b=$(($a/$b))" echo "a**b=$(($a**$b))" echo "a%b=$(($a%$b))" 整数测试表达式: 小题:使用read的交互方式,来比较两个整数的大小。 分析: 1、要求整数 2、2个数 3、比较大小 大于 等于 小于 [root@web01 scripts]# cat com1.sh #!/bin/bash read -p "请输入两个整数:" a b #1.判断是不是输入两个数 [ -z "$b" ] &&{ echo "请输入两个整数" exit 1 } #2.判断整数 expr $a + $b + 1 &>/dev/null [ $? -ne 0 ] &&{ echo "请输入两个整数" exit 2 } #3.判断是否大于 [ $a -gt $b ] &&{ echo "$a>$b" exit 0 } #4.判断是否等于 [ $a -eq $b ] &&{ echo "$a=$b" exit 0 } #5.判断是否小于 echo "$a<$b" [root@db03 scripts]# cat comp.sh #!/bin/bash ############################################################## # File Name: comp.sh # Version: V1.0 # Author: oldboy # Organization: www.oldboyedu.com # Created Time : 2018-05-30 11:36:19 # Description: ############################################################## read -p "Pls input two num:" a b #1.第一关判断传入的内容都是整数 expr $a + $b + 100 &>/dev/null [ $? -ne 0 ] &&{ echo "Usage:$0 num1 num2" exit 1 } #2.第二关输入两个数 [ -z "$b" ] &&{ echo "Usage:$0 num1 num2" exit 2 } #3.比较大小 [ $a -gt $b ] && { echo "$a>$b" exit 0 } [ $a -eq $b ] && { echo "$a=$b" exit 0 } echo "$a<$b" exit 0 ==============升级使用if [root@db03 scripts]# cat comp.sh #!/bin/bash ############################################################## # File Name: comp.sh # Version: V1.0 # Author: oldboy # Organization: www.oldboyedu.com # Created Time : 2018-05-30 11:36:19 # Description: ############################################################## read -p "Pls input two num:" a b #1.第一关判断传入的内容都是整数 expr $a + $b + 100 &>/dev/null [ $? -ne 0 ] &&{ echo "Usage:$0 num1 num2" exit 1 } #2.第二关输入两个数 [ -z "$b" ] &&{ echo "Usage:$0 num1 num2" exit 2 } #3.比较大小 if [ $a -gt $b ] then echo "$a>$b" elif [ $a -eq $b ] then echo "$a=$b" else echo "$a<$b" fi 逻辑测试表达式 !&& -a 并且 || -o 或者 []中 使用 -a -o [[]]或(())里面 使用&& || [] [[]] (()) 这些符号之间连接 使用&& || make && make install ==================== [root@db03 scripts]# [ 1 -eq 1 -a -f /etc/hosts ] && echo 1 || echo 0 1 [root@db03 scripts]# [ 1 -eq 2 -a -f /etc/hosts ] && echo 1 || echo 0 0 [root@db03 scripts]# [ 1 -eq 2 && -f /etc/hosts ] && echo 1 || echo 0 -bash: [: missing `]' 0 [root@db03 scripts]# [[ 1 -eq 2 && -f /etc/hosts ]] && echo 1 || echo 0 0 [root@db03 scripts]# [ 1 -eq 2 ] && [ -f /etc/hosts ] && echo 1 || echo 0 [root@db03 scripts]# [ -f /etc/hosts ] && echo 1 || echo 0 1 [root@db03 scripts]# [ ! -f /etc/hosts ] && echo 1 || echo 0 0 小题:如果/tmp/oldboy.sh是普通文件,并且可执行,就执行改脚本。 file="/tmp/oldboy.sh" if [ -f $file ] && [ -x $file ] then bash $file fi 1. 单分支结构 第一种语法: if <条件表达式> then 指令 fi 第二种语法: if <条件表达式>; then 指令 fi 如果 <你有房> 那么 我就嫁给你 果如 if条件句的双分支结构语法为: if <条件表达式> then 指令集1 else 指令集2 fi 如果 <你有房> 那么 我就嫁给你 否则 我再考虑下 果如 多分支: if <条件表达式1> then 指令1... elif <条件表达式2> then 指令2... elif <条件表达式3> then 指令3... else 指令4... fi 范例7_2:开发Shell脚本判断系统剩余内存的大小,如果低于100MB就邮件报警给系统管理员, 并且将脚本加入系统定时任务,即每3分钟执行一次检查。 分析思路: 1、取内存 free -m|awk 'NR==3{print $NF}' 2、发邮件 mail -s "主题" 邮件地址 </etc/hosts echo ""|mail -s "主题" 邮件地址 3、开发脚本判断发邮件 full="内存充足。" leak="内存不足。" free=`free -m|awk 'NR==3{print $NF}'` if [ $free -lt 1000 ] then echo "$leak"|tee /tmp/mail.log mail -s "$leak`date`" 31333741-@qq.com </tmp/mail.log else echo "$full" fi 4、加入定时任务 [root@db03 scripts]# crontab -l|tail -2 ########### */3 * * * * /bin/sh /server/scripts/judge.sh >/dev/null 2>&1 课后练习:开发Shell脚本判断系统根分区剩余空间的大小,如果低于1000MB报警空间不足, 如果低于500M,就报警空间严重不足,最后把结果邮件发给系统管理员, 并且将脚本加入系统定时任务,即每3分钟执行一次检查。 预习: 函数 case for while

    Shell编程第一章节前六小节复习

    
    Shell编程实战:第一模块前六节复习:Shell编程基础
    
    01-第一章:如何学好shell编程。
    必备基础:
    1)熟练vim编辑器,.vimrc配置。
    2)120个左右Linux命令。(跟老男孩学习Linux运维:核心命令实战)
    3)网络服务,rsync,nfs,lamp,lnmp,ltmp,keepalived,lvs
    
    如何学好:
    1、锻炼编程思维
    比如要先有大纲 第一章
    1.1 1.1.1 1.1.2 第二章 2.1 2.1.1 2.1.2 2、多练,多思考,不要拿来主义。 3、有一本教科书。跟老男孩学习Linux运维:Shell编程实战 02-第二章:Shell基础入门 什么是shell 什么是shell脚本 shell脚本的建立 #!/bin/bash #### shell脚本的执行 4种方式: 1)bash/sh oldboy.sh #推荐使用 2)source/. oldboy.sh ***** 3sh/bash<oldboy.sh 4)./oldboy.sh或/server/scripts/oldboy.sh #必须要有执行权限。 Shell的执行过程 oldboy.sh: pwd ls oldgirl.sh #子shell ls oldgirl.sh & 放到后台执行。 并行,执行的同时,也执行下面的额ls 重点也是难点 source/. oldgirl.sh 相当于子Shell和父Shell相当于在一个shell里运行。 03-第三章 变量基础 1、什么是变量? oldboy="I am oldboy" oldboy是变量名,= 是赋值符号,"I am oldboy"是变量的内容。 OldboyAge oldboyAge #推荐。 oldboy_age 2、变量类型,shell里面是弱类型 decalre -i A #定义一个整形,一般没有这种需求 3、变量分类 环境变量(全局变量) 特点:大写 全局生效 1)系统的环境变量   PS1,PATH,LANG 直接用。 2)自定义环境变量 export OLDBOY=1 OLDBOY=1 export OLDBOY decalre OLDBOY=1 查看: env/set echo $变量名 取消: unset 变量名 普通变量(局部变量) 局部生效: oldboy="I am oldboy" oldboy是变量名,=是赋值符号,"I am oldboy"是变量的内容。 变量名: OldboyAge oldboyAge #推荐。 oldboy_age = 两边不能有空格 变量内容: 4种方式 a=test #不加引号,也会解析变量,推荐连续数字用此方法。 a='test' #所见即所得。 a="test" #默认双引号,解析变量。 a=`date` #解析一个命令结果。就用``或$()。 输出变量: $oldboy_age ${oldboy_age}dddd 04-第四章 变量的扩展 1)特殊位置变量 $0 ***** 企业应用-脚本的结尾 $1,$2,$n ***** $# ***** 获取参数的个数 $* 两个的相同点和不同点 $@ 2)特殊状态变量 $? ***** $$ 进程号 $!上一个脚本的进程 $_ 上一个脚本的最后一个参数 相当于 esc+点 3)变量的子串 ${oldboy} ${#oldboy} wc -L ,expr length "$oldboy",awk '{print length }' ${oldboy:2} ${oldboy:2:2} ${oldboy#word} ${oldboy##word} ${oldboy%word} ${oldboy%%word} ${oldboy/dd/ff} ${oldboy//dd/ff} 4)变量特殊扩展 result=${oldboy:-word} # 推荐 **** result=${oldboy:=word} result=${oldboy:?word} result=${oldboy:+word} 霸道 第五章 变量的数值计算 整数: (()) 推荐 let 推荐 $[] expr declare declare -i a=4+3 echo $a 小数: bc 推荐加减 awk 乘除推荐 expr1)判断是否为整数。 2)扩展名 。忘记判断,记忆窍门 vim ·which ssh-copy-id· 3)取字符串长度 变量的定义: 1)a=1 2)传参 3)read读入 read -t 10 -p "输入提示:" a 用途:菜单选择,和用户交互
    第六章 条件测试与比较 6种: [] #推荐 [[]] test (()) (命令) `命令` 等价if单分支 []
    &&{ dddd } 等价if双分支 []&&{ dddd }||{ ddd } =================================== [ -f /etc/hosts ] || exit 5 常用的推荐用法,简简洁,命令多的时候,用if比较好 make && make install ================================= if,whlie 当做一个条件判断。 文件、字符、整数 测试 逻辑符
    都可以用man test 找到

    if、case、while、for、函数、数组和企业实践

    if 条件语句

    if是最常见的条件判断语句

    例1:如果不存在/backup目录就创建。
    [root@web-01 /server/tools]# vim 07-01.sh
    #!/bin/bas
    path=/backup
    [ -d $path ] || mkdir $path -p
    # 相当于
    # :冒号表示什么也不干
    if [ -d $path ]
    then
            :
    else
            mkdir $path -p
    fi
    # 或者
    [ !-d $path ] && mkdir /backdir -p
    # 相当于
    if [ !-d $path ]
    then
            mkdir /backdir -p
    if
    "07-01.sh" 18L, 248C writte
    vim命令:替换:%s#/backup#$path#g
    例2:开发Shell脚本判断系统剩余内存的大小,如果低于100MB就提示内存不足,否则提示内存充足。 考查if双分支: 分析:
    1)提取系统内存。 2)if判断, [root@web01 07]# cat 07_02.sh #!/bin/bash ############################################################## # File Name: 07_02.sh # Version: V1.0 # Author: pizza # Created Time : 2018-06-06 22:58:54 # Description: ############################################################## mem=`free -m|awk 'NR==3{print $NF}'` if [ $mem -lt 1000 ] then echo "内存严重不足。" else echo "内存还够" fi

    在第一章的中添加了内存不足发邮件的例子
    例3:分别使用变量定义、read读入及脚本传参方式实现比较2个整数的大小。 1)变量定义: a=6 b=2 2)read读入: read -p "请输入两个数字:" a b 3)传参: 分析: 大于 等于 小于 #!/bin/bash
    ##############################################################
    # File Name: 07-03.sh
    # Version: V1.0
    # Author: pizza
    # Created Time : 2019-03-28 21:20:46
    # Description:
    ##############################################################
    # 第一种,传参
    a=6
    b=7
    # 第二种,read
    read -p "请输入两个数字" a b
    # 第三种,参数
    $a=$1
    $b=$2
    # 判断输入
    # 1、判断数字是不是够,判断b是不是空,或者使用$#
    # 2、判断是不是整数,利用expr
    # 3、

    if [ $a -gt $b ]
    then
        echo "$a>$b"
    elif [ $a -lt $b ]
    then
        echo "$a<$b"
    else
        echo "$a=$b"
    fi

    例4:打印一个菜单如下,当用户选择对应的数字时,就执行对应项的应用。
    1.install lamp 2.install lnmp 3.exit 第一章已讲解: bash内置核心命令read的企业级应用实践 read企业应用 [root@web01 scripts]# cat select1.sh #!/bin/bash cat <<EOF 1.install lamp 2.install lnmp 3.exit EOF read -p "请选择一个序号(必须是数字):" num #1.判断是否为整数 expr 2 + $num &>/dev/null if [ $? -ne 0 ] then echo "Usage:$0 {1|2|3}" exit 1 fi #2.判断执行处理 if [ $num -eq 1 ] then echo "install lamp..." elif [ $num -eq 2 ] then echo "install lnmp..." elif [ $num -eq 3 ] then echo "bye." exit else echo "Usage:$0 {1|2|3}" exit 1 fi

    Shell函数的知识与实践

    函数的作用就是将程序里多次被调用的相同代码组合起来,并为其取个名字。其他所有想重复调用这部分代码的复方都只需要调用这个这个名字就可以了。当需要修稿这部分重复代码的时候,也只需要改变函数内的一份代码即可实现所有的修改,也可以把函数独立写到文件里面,当需要调用的时候,再加载进来。

    函数的语法体

    oldboy() {
        echo "I am oldboy."
    }
    function oldgirl {
        echo "I am oldgirl."
    }
    test() {
        echo "Hello world."
    }
    oldboy
    oldgirl

    [root@web-01 ~]# sh test_hanshu.sh
    I am oldboy.
    I am oldgirl.
    Hello world.
    [root@web-01 ~]#

    向函数传参

    带参数的函数编写执行
    oldboy() {
        echo "I am $1."
    }
    oldboy oldboy
    将函数传参转为脚本传参
    oldboy() {
        echo "I am $1."
    }
    oldboy $1

    函数和执行函数分离
    ./etc/init.d/function
    pizza $1

    Shell函数的执行注意事项

    企业案例:通过脚本传参的方式,检查Web 网站URL是否正常。

    wget命令:
    --spider 模拟爬虫
    -q 安静访问
    -o /dev/null 不输出
    -T --timeout 超时时间
    -t --tries 重试次数
    [root@web01 ~]# wget --spider -T 5 -q -o /dev/null -t 2 www.baidu.com
    [root@web01 ~]# echo $?
    0
    curl命令:
    -I 看响应头
    -s 安静的 安静模式。不显示进度表或错误信息。使cURL 不反馈信息。
    -o /dev/null 不输出
    -w %{http_code} 返回状态码,200
    -m 超时时间
    [root@web01 ~]# curl www.baidu.com -s &>/dev/null [root@web01 ~]# echo $? 0 [root@web01 ~]# curl -I -m 5 -s -w "%{http_code} " -o /dev/null www.baidu.com 200 不用函数的实现写法 #!/bin/sh if [ $# -ne 1 ] then echo $"usage:$0 url" exit 1 fi wget --spider -q -o /dev/null --tries=1 -T 5 $1 #<==-T指定超时时间,这里的$1为脚本的参数。 if [ $? -eq 0 ] then echo "$1 is yes." else echo "$1 is no." fi 高端专业的函数写法: [root@oldboy ~]# cat checkurl.sh #!/bin/bash ############################################################## # File Name: checkurl.sh # Version: V1.0 # Author: oldboy # Organization: www.oldboyedu.com # Created Time : 2018-06-07 18:29:19 # Description: ############################################################## usage(){ echo "Usage:$0 url" exit 1 } checkurl(){ wget -q -o /dev/null -t 2 -T 5 $1 if [ $? -eq 0 ] then echo "$1 is ok" else echo "$1 is fail" fi } main(){ if [ $# -ne 1 ] then usage fi checkurl $1 } main $* [root@oldboy scripts]# cat 8_5_1.sh #!/bin/sh function usage() { #<==帮助函数 echo $"usage:$0 url" exit 1 } function check_url() { #<==检测URL函数。 wget --spider -q -o /dev/null --tries=1 -T 5 $1 #<==这里的$1就是函数传参。 if [ $? -eq 0 ] then echo "$1 is yes." else echo "$1 is no." fi } function main() { #<==主函数。 if [ $# -ne 1 ] #<==如果传入的多个参数,则打印帮助函数,提示用户。 then usage fi check_url $1 #<==接收函数的传参,即把结尾的$*传到这里。 } main $* #<==这里的$*就是把命令行接收的所有参数作为函数参数传给函数内部,常用手法。

    CASE结构条件句

    几乎所有的case都可以用if替代
    case "$1" in
        1)
        dddd
        ;;
        2)
        dddd
        ;;
        *)
        dddd
    esac
    
    企业应用:
    启动脚本
    read 读入 菜单选择。
    
    范例9_2:执行shell脚本,打印一个如下的水果菜单:
    1.apple
    2.pear
    3.banana
    4.cherry
    当用户输入对应的数字选择水果的时候,告诉他选择的水果是什么,并给水果单词加上一种颜色(随意),要求用case语句实现。
    范例9_3:给内容加不同的颜色。
    内容的颜色用数字表示,范围为30-37,每个数字代表一种颜色。代码如下:
    echo -e "33[30m 黑色字 33[0m" #<==30m表示黑色字。
    echo -e "33[31m 红色字 33[0m" #<==31m表示红色字。
    echo -e "33[32m 绿色字 33[0m" #<==32m表示绿色字。
    echo -e "33[33m 棕色字 33[0m" #<==33m表示棕色字(brown),和黄色字相近。
    echo -e "33[34m 蓝色字 33[0m" #<==34m表示蓝色字。
    echo -e "33[35m 洋红字 33[0m" #<==35m表示洋红色字(magenta),和紫色字相近。
    echo -e "33[36m 蓝绿色 33[0m" #<==36m表示蓝绿色字(cyan),和浅蓝色字相近。
    echo -e "33[37m 白色字 33[0m" #<==37m表示白色字。
    说明:不同的数字对应的字体颜色,见系统帮助(来源man console_codes命令的结果)。
    范例9_6: 给输出的字符串加不同的背景颜色。
    字的背景颜色对应的数字范围为40-47,代码如下。
    echo -e "33[40;37m 黑底白字33[0m"   #<==40m表示黑色背景。
    echo -e "33[41;37m 红底白字33[0m"   #<==41m表示红色背景。
    echo -e "33[42;37m 绿底白字33[0m"   #<==42m表示绿色背景。
    echo -e "33[43;37m 棕底白字33[0m"   #<==43m表示棕色背景(brown),和黄色背景相近。
    echo -e "33[44;37m 蓝底白字33[0m"   #<==44m表示蓝色背景。
    echo -e "33[45;37m 洋红底白字33[0m"  #<==45m表示洋红色背景(magenta),和紫色背景相近。
    echo -e "33[46;37m 蓝绿底白字33[0m"   #<==46m表示蓝绿色背景(cyan),和浅蓝色背景相近。
    echo -e "33[47;30m 白底黑字33[0m"    #<==47m表示白色背景。
    
    创建case3.sh,定义函数 ,执行函数color $*
    创建case4.sh,用.或者source调用函数,打印菜单,输入数字,显示不同颜色的字体 范例9_10:利用case语句开发Rsync服务启动停止脚本,本例采用case语句以及新的思路来实现。 分析: 启动: rsync
    --daemon 停止: pkill rsync killall rsync kill 进程号 ---->最专业的方法 /etc/init.d/rsyncd {start|stop|restart} case

    rsync.sh简单版

    case "$1" in
        start)
            rsync --deamon
            if [ $? -eq 0 ]
            then
                echo "rsync startup ok"
            else
                echo "rsync startup fail"
            fi
            ;;
        stop)
            killall rsync
            if [ $? -eq 0 ]
            then
                echo "rsync stop ok"
            else
                echo "rsync stop fail"
            fi
            ;;
        restart)
            killall rsync && sleep 1 && rsync --deamon
            if [ $? -eq 0 ]
            then
                echo "rsync restart ok"
            else
                echo "rsync restart fail"
            fi
            ;;
    
        *)
            echo "usage:$0 {start|stop|restart}"
            exit 1
    esac

    升级版

     start(){                                                                                                        
         rsync --deamon
         retval=$?
         if [ $retval -eq 0 ]
         then
             echo "rsync startup ok"
             return $retval
         else
             echo "rsync startup fail"
             return $retval
         fi
     }
     stop(){
         killall rsync
         retval=$?
         if [ $? -eq 0 ]
         then
              echo "rsync stop ok"
                                                                                                      9,1           Top
            return $retval
        else
            echo "rsync restart fail"
            return $retval
        fi  
        }   
    case "$1" in
        start)
            start
            # 为了向外传值
            retval=$?
            ;;  
        stop)
            stop
            retval=$?
            ;;  
        restart)
            restart
            retval=$?
            ;;  
    
        *)  
            echo "usage:$0 {start|stop|restart}"
            exit 1
    esac

    想要使用chkconfig(man ckhconfig),找到这些信息

    RUNLEVEL FILES
           Each service which should be manageable by chkconfig needs two or more commented lines added to its init.d
           script. The first line tells chkconfig what runlevels the service should be started in by default, as well
           as the start and stop priority levels. If the service should not, by default, be started in any runlevels,
           a  -  should  be used in place of the runlevels list.  The second line contains a description for the ser‐
           vice, and may be extended across multiple lines with backslash continuation.
    
           For example, random.init has these three lines:
           # chkconfig: 2345 20 80  表示在2345这几个启动级别上 启动顺序排20 停止顺序排80
           # description: Saves and restores system entropy pool for 
           #              higher quality random number generation.

    在脚本首行加入

    #!/bin/bash
    # chkconfig: 2345 20 80
    # description: rsync start stop and restart   

    将脚本移动到/etc/init.d/下,并添加执行权限,添加到chkconfig中才能使用

    [root@web-01 /server/tools]# mv /etc/init.d/rsync_up.sh /etc/init.d/rsyncd
    [root@web-01 /server/tools]# chmod +x /etc/init.d/rsyncd
    [root@web-01 /server/tools]# chkconfig --list rsyncd
    
    Note: This output shows SysV services only and does not include native
          systemd services. SysV configuration data might be overridden by native
          systemd configuration.
    
          If you want to list systemd services use 'systemctl list-unit-files'.
          To see services enabled on particular target use
          'systemctl list-dependencies [target]'.
    
    service rsyncd supports chkconfig, but is not referenced in any runlevel (run 'chkconfig --add rsyncd')
    [root@web-01 /server/tools]# chkconfig --add rsyncd
    [root@web-01 /server/tools]# chkconfig --list rsyncd
    
    Note: This output shows SysV services only and does not include native
          systemd services. SysV configuration data might be overridden by native
          systemd configuration.
    
          If you want to list systemd services use 'systemctl list-unit-files'.
          To see services enabled on particular target use
          'systemctl list-dependencies [target]'.
    
    rsyncd             0:off    1:off    2:on    3:on    4:on    5:on    6:off

    更好一些,调用了系统函数,action。并优化了重复停止的输出 

    #!/bin/bash                                                       
    # chkconfig: 2345 20 80
    # description: rsync start stop and restart
    ##############################################################
    # File Name: syncd.sh
    # Version: V1.0
    # Author: pizza
    # Created Time : 2019-03-29 22:06:46
    # Description:
    ##############################################################
    . /etc/init.d/functions
    start(){
        rsync --deamon
        retval=$?
        if [ $retval -eq 0 ]
        then
            action  "rsync startup ok" /bin/true
            return $retval
        else
            action "rsync startup fail" /bin/false
            return $retval
        fi
    }
                                                    1,1           Top
            return $retval
        fi
        }
    case "$1" in
        start)
            start
            # 我了向外传值
            retval=$?
            ;;
        stop)
            stop
            retval=$?
            ;;
        restart)
            restart
            retval=$?
            ;;
    
        *)
            echo "usage:$0 {start|stop|restart}"
            exit 1
    esac
         

    开发和系统媲美的脚本

    跟完善的查看企业实践题第11题-制作MySQL脚本

                                                             92,0-1        Bot
    #!/bin/bash
    # chkconfig: 2345 20 80
    # description: rsync start stop and restart
    ##############################################################
    # File Name: syncd.sh
    # Version: V1.0
    # Author: pizza
    # Created Time : 2019-03-29 22:06:46
    # Description:
    ##############################################################
    # 定义锁文件
    lockfile=/var/lock/subsys/rsyncd
    # 定义变量,指定rsyncd的的pid,是需要自己rsync的conf中去创建
    
    #[root@web-01 /server/tools]# vim /etc/rsyncd.conf
    #pid file=/var/run/rsyncd.pid    
    
    srsync_pid_file_path=/var/run/rsyncd.pid
    . /etc/init.d/functions
    start(){
        rsync --deamon
        retval=$?
        if [ $retval -eq 0 ]
        then
            action  "rsync startup ok" /bin/true
            touch $lockfile
            return $retval
        else
            action "rsync startup fail" /bin/false
            return $retval
        fi
    }
    stop(){
        # 为了在重复停止操作的时候,不提示,将其扔到黑洞
        if test -s "$rsyncd_pid_file_path"
        then
            rsyncd_pid=`cat $rsyncd_pid_file_path`
            # 判断进程号是不是真实存在
            if (kill -0 $rsyncd_pid &>/dev/null)
            then
                kill $rsyncd_pid
    
                retval=$?
                if [ $? -eq 0 ]
                then
                    action "rsync stop ok" /bin/true
                    rm -f $lockfile
                    return $retval
                else
                    action "rsync stop fail" /bin/false
                    return $retval
                fi
            else
                echo "rsyncd process is not exist."
                return 2
            fi
        else
            echo "$srsync_pid_file_path is not exits,or rsyncd doesnot start"
        fi
    }
    restart(){
        killall rsync && sleep 1 && rsync --deamon
        retval=$?
        if [ $? -eq 0 ]
        then
            action "rsync restart ok" /bin/true
            return $retval
        else
            action "rsync restart fail" /bin/false
            return $retval
        fi
        }
    case "$1" in
        start)
            start
            # 我了向外传值
            retval=$?
            ;;
        stop)
            stop
            retval=$?
            ;;
        restart)
            restart
            retval=$?
            ;;
    
        *)
            echo "usage:$0 {start|stop|restart}"
            exit 1
    esac
      

    case总结

    1、case语句和if条件句的使用性

    case语句比较适合变量较少且为固定的数字或者字符串集合的情况(非不确定内容,如范围)

    2、if和case的常用应用场景

    case只要写服务的启动脚本,一般情况下,传参不同且具有少量的字符串,其使用范围较窄

    if就是取值判断、比较,应用比case更广,几乎所有的case语句都可以用if条件语句实现。

    3、case语句的特点和优势

    它相当于多分支的if/elif/else语句,但是case语句的优势是更规范。易读

    while循环

    循环语句命令常用于重复执行一条指令或一组指令,直到条件不满足停止,Shell脚本语言的循环语句常见的有while、until、for以及select循环语句。 while循环语句主要用来重复执行一组命令或语句,在企业实际应用中,常用于守护进程或持续运行的程序,除此以外,大多数循环都会用后文即将讲解 的for循环语句。

    while true
    do
        uptime >> /tmp/uptime.log
        sleep 2 # 暂停2s
        usleep 2000 # 微秒                                                                                              
    done

    脚本进程管理命令

     

        后台运行  &、nohup、screen(运维人员)

     为什么要用后台运行,防止你在执行重要命令的时候,网络宕机

    进程管理的其他常见命令

    范例1:请使用while循环对下面的脚本进行修改,是的当执行脚本时,每次执行完脚本后,不退出脚本,而是提示用户输入

    while true
    do
        read -p "请输入两个数字:" a b
        if [ -z $b ]
        then                                                                                
            echo "请输入两个数字"
            continue
        fi
        expr 10 + $a + $b &>/dev/null
        if [ $? -ne 0 ]
        then
            echo "请输入两个数字"
            continue
        fi
        echo "a+b=$(($a+$b))"
    done

    范例2:猜数字游戏。首先让系统随机生成一个数字,给这个数字定一个范围(1-60),让用户输入猜的数字,对输入进行判断,如果不符合要求,就给予高或低的提示,猜对后则给出猜对用的次数,请用while语句实现。

    提示:可以赋予一个猜水果的价格游戏。

    1、给数字定范围(1-60)
    RANDOM随机数,它的范围是0-32767
    [root@web-01 /server/tools]# echo $RANDOM
    9279
    为随机数取模。控制在1-60
    [root@web-01 /server/tools]# echo $((RANDOM%60))
    11
    2、用户输入数字
    read -p “请输入数字:” num
    
    3、对比,直到正确才推出
    while 
    4、代码:
    random="$(($RANDOM%60))"
    #做计数
    count=0
    echo $random
    while true
    do
        read -p "请输入数字:" num
        ((count++))
        if [[ $num -lt $random ]]
        then
            echo "低了"
            continue
        elif [[ $num -gt $random ]]
        then
            echo "高了"
            continue
        elif [[ $num -eq $random ]]                                          
        then
            echo "恭喜你,猜对了,一共猜了$count次"
            exit 0
        else
            echo "请输入数字"
        fi
    done

    还可以加入函数,其他的限制内判断,使脚本更完善

    范例3:分析Apache访问日志(access_2010-12-8.log)日志每行访问字节数对应字段数字相加,计算出的访问量。给出实现程序,请用while循环实现。(3分钟)

    方式1:在while循环结尾done通过输入重定向指定读取的文件。
    while read line
    do
        cmd
    done<FILE
    方式2:使用cat读取文件内容,然后通过管道进入while循环处理。
    cat FILE_PATH|while read line
    do
        cmd
    done
    方式3:采用exec读取文件后,然后进入while循环处理。
    exec <FILE
    sum=0
    while read line
    do
        cmd
    done

    体验

    while read line
    do
        echo $line
        sleep 1
    done < ./while_01.sh                                                     
    ~                                                                        
    ~                                                                        
                                                                          
    "while_readline.sh" [New] 13L, 320C written            
    [root@web-01 /server/tools]# sh while_readline.sh
    #!/bin/bash   
    ##############################################################
    # File Name: while_01.sh
    # Version: V1.0
    # Author: pizza
    # Created Time : 2019-03-30 07:19:26
    # Description:
    ##############################################################
    while true
    do
    uptime >> /tmp/uptime.log
    sleep 2 # 暂停2s
    usleep 2000 # 微秒
    done

    体验2

    [root@web-01 /server/tools]# seq 10 >>num.log
    脚本
    sum=0
    while read line
    do
        ((sum+=line))
    
    done<./num.log
    echo $sum     
    执行
    [root@web-01 /server/tools]# sh while_num_add.sh
    55

    答案

    sum=0
    awk '{print $10}' access_2010-12-8.log |grep -v -|while read line
    do
        ((sum+=line))
    done
    echo sum 
    # 已经计算了,但是最后的结果是0
    # 这是因为执行了子shell
    # 使用下面的方法可以成功输出                                             
    
    sum=0
    awk '{print $10}' access_2010-12-8.log |grep -v - > any_sum.log
    do
        ((sum+=line))
    done<./any_sum.log
    echo sum 

    小结

    1、while循环的特长是执行守护进程,以及实现我们希望循环不退出持续执行的应用

    擅长用于频率小于1分钟循环处理,其他的while循环几乎都可以被for以及定时任务crond替代

    2、if、for最常用,然后是while(守护进程),case(服务启动脚本)

    Shell脚本中各个语句的使用场景

      1、条件表达式,用于简短的条件判断及输出(文件是否存在,字符串是否为空)

      2、if取值判断,多用于不同值数量较少的情况

      3、for正常的循环应用处理,最常用

      4、while多用于守护进程,无限循环(要加sleep,usleep,控制pinlv)应用

      5、case 多用于服务启动脚本,打印菜单可用select语句,不过很少用,都用cat的here文档方法替代

      6、函数用途主要指编码逻辑清晰,减少重复语句开发

    for循环

    for循环语句和while循环语句类似,但for循环语句主要用于执行次数有限的循环,而不是用于守护进程以及无限循环。for循环语句常见的语法有两种, 下面将在不同的语法中对for循环语句进行详尽的讲解。

    ..

    范例1:用for循环竖向打印1、234、5共5个数字。
    范例2:通过开发脚本实现仅设置sshd rsyslog crond network sysstat服务开机自启动。
    范例3:计算从1加到100之和。
    范例4:在Linux下批量修改文件名,将文件名中的“_finished”去掉。
    准备测试数据,如下。
    [root@oldboy test]# mkdir /oldboy -p
    [root@oldboy test]# cd /oldboy
    [root@oldboy oldboy]# touch stu_102999_1_finished.jpg stu_102999_2_finished.jpg stu_102999_3_finished.jpg 
    [root@oldboy oldboy]# touch stu_102999_4_finished.jpg stu_102999_5_finished.jpg
    [root@oldboy oldboy]# ls -l
    总用量 0
    -rw-r--r-- 1 root root 0 9月   5 10:43 stu_102999_1_finished.jpg
    -rw-r--r-- 1 root root 0 9月   5 10:43 stu_102999_2_finished.jpg
    -rw-r--r-- 1 root root 0 9月   5 10:43 stu_102999_3_finished.jpg
    -rw-r--r-- 1 root root 0 9月   5 10:43 stu_102999_4_finished.jpg
    -rw-r--r-- 1 root root 0 9月   5 10:43 stu_102999_5_finished.jpg
    
    ls *.jpg|awk -F "_finished" '{print "mv",$0,$1$2}'|bash
    rename "_finished" "" *.jpg
    for file in `ls ./*.jpg`
    do
        mv $file `echo ${file/_finished/}`
    done

    范例1答案

    for n in 1 2 3 4 5  或者 {1..5}  或者seq 5 
    do
        echo $n
    done
    echo "---------"
    for ((i=1;i<=5;i++))
    do
        echo $i
    done                                                                                                                                                                                                                                                                               
    "for_printnum.sh" [New] 18L, 350C written              
    [root@web-01 /server/scripts]# sh for_printnum.sh
    1
    2
    3
    4
    5
    ---------
    1
    2
    3
    4
    5

    范例2答案

    for name in sshd rsyslog crond network sysstat
    do
        echo "chkconfig $name on"                                            
    done

    相当于在命令行执行
    [root@web-01 /server/scripts]# chkconfig |grep 3:on |awk '{print "chkconfig",$1,"off"}'|bash

    范例3

    1、for循环
    2、while循环
    3、算法 

    范例4

    1mv $file 'echo ${file/_finished/}'
    2、不用for循环
    [root@web-01 /server/scripts]# ls *.jpg|awk -F "_finished" '{print $0,$1$2}'
    3、rename  from  to  file
    rename  _finished ""  *.jpg

    循环控制语句break、continue、exit、return

    上述命令中breakcontinue在条件语句及循环语句(forwhileif等)中用于控制程序的走向,

    exit用于终止所有语句并退出当前脚本,除此之外,exit还可以返回上一次程序或命令的执行状态值给当前Shell

    return类似exit,只不过return用于函数内部返回函数执行的状态值

    break n    如果省略n表示跳出整个循环,n 表示跳出循环的层数
    continue n    如果省略n表示跳过本次循环,忽略本次循环的剩余代码,进入循环的下一次循环。n 表示退到第n层继续循环
    exit n    退出当前shell程序,n为上一次程序执行的状态返回值。n也可以省略,再下一个shell里可通过$?接收exit n的n值
    return n    用于在函数里,作为函数的返回值,用于判断函数执行是否正确。再下一个shell里可通过$?接收exit n的n值

    Shell编程数组应用实践

    为什么会产生shell数组

    通常在开发Shell脚本时,我们定义变量采用的形式为a=1;b=2;c=3,可如果有多个变量呢?这时再一个一个定义很费劲,并且多个不确定的变量内容,也难以进行变量定义,此外快速读取不同变量的值也是一件很痛苦的事情,于是数组就诞生了,就是为了解决上述问题而来的

    什么是Shell数组

    如果读者有过其他语言的编程经历,那么想必会熟悉数组的概念。简单地说,Shell的数组就是把有限个元素(变量或字符内容)用一个名字命名,然后用编号对它们进行区分的元素集合。这个名字就称为数组名,用于区分不同内容的编号就称为数组下标。组成数组的各个元素变量称为数组的元素,有时也称为下标变量。

    有了Shell数组后,就可以用相同名字引用一系列变量及变量值,并通过数字(索引)来识别使用它们。在许多场合,使用数组可以缩短和简化程序开发。


    数组的本质还是变量,是特殊的变量形式
    array=(1 2 3 4 5)

    Shell数组的定义*****
        方法1:推荐,用小括号把变量值括起来赋值给数组变量,中间用空格分割
        array=(one two three four)
        方法2:用小括号把变量值括起来,同时采用键值对的形式赋值
        array=([0]=one [1]=two [2]=three [3]=four)
        方法3:通过分别定义数组变量的方式
        [root@web01 ~]# array[0]=one
        [root@web01 ~]# array[1]=two
        [root@web01 ~]# array[2]=three
        [root@web01 ~]# array[3]=four
        [root@web01 ~]# echo ${array[@]}
        one two three four
        方法4:命令的结果放到数组里,推荐。动态定义数组变量,使用命令的输出结果作为数组的内容
        array=(`ls /server/scripts`)
    
    说明:还可以使用declare -a array来定义数组类型,但是比较少这样用。
    
    
    操作数组元素
    打印单个数组元素用${数组名[下标]},当未指定数组下标时,数组的下标是从0开始。
    使用*或者@可以得到整个数组内容。
    用${#数组名[@或*]}可以得到数组长度,这和前文讲解的变量子串知识是一样的,因为数组也是变量,只不过是特殊的变量,因此也适合变量的子串替换等知识。
    
    读取数组内容:*****
    [root@web01 ~]# array=( 1 2 3 4 5)
    [root@web01 ~]# echo ${array[0]}
    1
    [root@web01 ~]# echo ${array[1]}
    2
    [root@web01 ~]# echo ${array[2]}
    3
    [root@web01 ~]# echo ${array[3]}
    4
    [root@web01 ~]# echo ${array[4]}
    5
    [root@web01 ~]# echo ${array[5]}
    
    [root@web01 ~]# echo ${array[*]}
    1 2 3 4 5
    [root@web01 ~]# echo ${array[@]}
    1 2 3 4 5
    [root@web01 ~]# echo ${#array[@]}
    5
    [root@web01 ~]# echo ${#array[*]}
    5
    
    给数组增加内容:
    [root@web01 ~]# array[5]=oldboy   <==增加下标为5的数组元素。
    [root@web01 ~]# echo ${#array[*]}
    6
    [root@web01 ~]# echo ${array[*]}
    1 2 3 4 5 oldboy
    
    删除数组元素:
    [root@web01 ~]# unset array[1]
    [root@web01 ~]# echo ${array[*]}
    1 3 4 oldboy
    [root@web01 ~]# unset array[0]
    [root@web01 ~]# echo ${array[*]}
    3 4 oldboy
    
    数组赋值:
    [root@web-01 /server/scripts]# array[4]=999
    [root@web-01 /server/scripts]# echo ${array[*]}
    1 2 3 4 999 6
    
    数组的删除:
    因为数组本质上还是变量,因此可通过“unset 数组[下标]”清除相应的数组元素,
    如果不带下标,表示清除整个数组的所有数据。
    [root@web-01 /server/scripts]# unset array[1]  删除单个元素
    [root@web-01 /server/scripts]# echo ${array[*]}
    1 3 4 999 6
    [root@web-01 /server/scripts]# unset array    删除整个数组
    [root@web-01 /server/scripts]# echo ${array[*]} 没有数据输出了
    
    数组内容的截取和替换:
    这里和前文变量子串的替换是一样的,因为数组是特殊的变量
    [root@web-01 /server/scripts]# echo ${array[*]:1:3}  从下表为1的元素截取3个元素
    2 3 4
    替换 和sed命令,和变量子字符串的替换 很像,是一样的
    [root@web-01 /server/scripts]# echo ${array[*]/1/999}
    999 2 3 4 5 6 7 8 9 9990
    [root@web-01 /server/scripts]# echo ${array[*]}  该操作不会改变原数组,要改变请参考赋值修改
    1 2 3 4 5 6 7 8 9 10
    
    数组也是变量,因此也适合于前面讲解过的变量的子串处理的功能应用。
    数组的其他相关知识通过man bash然后搜Arrays来了解。
    
    数组元素部分内容的删除如下:
    [root@oldboy data]# array=(one two three four five)
    [root@oldboy data]# echo ${array[@]}               
    one two three four five
    [root@oldboy data]# echo ${array[@]#o*}    #<==从左边开始匹配最短的,并删除。
    ne two three four five
    [root@oldboy data]# echo ${array1[@]##o*}  #<==从左边开始匹配最长的,并删除。
    two three four five
    [root@oldboy data]# echo ${array[@]%f*}    #<==从右边开始匹配最短的,并删除。
    one two three
    [root@oldboy data]# echo ${array[@]%%f*}   #<==从右边开始匹配最长的,并删除。
    one two three
    
    使用for循环打印数组元素
    array=(1 2 3 4 5)
    for n in ${array[*]}
    do
        echo $n
    done
    echo =====================
    #i为数组下标
    for ((i=0;i<${#array[*]};i++))
    do
        echo ${array[i]}
    done 
    
    array=([1]=one [2]=two [3]=three) 
    array[0]=a;array[1]=b;array[2]=c
    array=($(命令))
    或
    array=(`命令`)

    shell数组企业面试题

    1、利用bash for循环打印下面这句话中字母数不大于6的单词(某企业面试真题)。

    I am pizza teacher welcome to luffy training class

    array=(I am oldboy teacher welcome to oldboy training class)
    
    for word in ${array[*]}
    #或者
    #for ((i=0;i<=${
    array[*];i++})) do if [ ${#word} -lt 6 ]
      # if [ ${#array[i]} -lt 6 ]
    then echo $word
       # echo
    array[i] fi done
    第十四章 编程规范
    第十五章 脚本调试
    第十六章 vim配置
    第十七章 trap
    第十八章 expect
    第十九章 企业案例实战
    
    第十九章 
    企业面试题1:
    分析:
    1、获取随机小写字符。
    echo $RANDOM$(date +%N)|md5sum|tr "[0-9]" "a-z"|cut -c 2-11
    
    2、for循环
    
    企业面试题3:
    101..10
    [root@db03 ~]# seq -w 10
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    [root@db03 ~]# echo ${01..10}
    -bash: ${01..10}: bad substitution
    [root@db03 ~]# echo {01..10}
    01 02 03 04 05 06 07 08 09 10
    
    2echo $RANDOM|md5sum|cut 1-8
    3、for循环批量设置用户和密码。
    
    企业面试题4:
    
    1、如何判断机器是否是活的。
    ping 10.0.0.53 -c 2 -W 3 有返回。
    nmap -sP 10.0.0.0/24
    
    2.for循环
    
    #!/bin/sh
    CMD="nmap -sP"
    Ip="10.0.0.0/24"
    $CMD $Ip|awk '/Nmap scan report for/ {print $NF}'
    
    http://blog.51cto.com/oldboy/1632876
    10,11,18,19,2013,6

    shell练习

    1、批量创建带有随机小写字符文件程序

    使用for循环在/pizza目录下创建10个html文件,其中每个文件包含10个随机小写字母加固定字母_pizza

     1、思路分析:

    核心是:创建10个随机小写字母

    第一种:$RANDOM

    [root@web-01 /server/scripts]# echo $RANDOM
    9839  范围0-32767 ,第一个容易被破解,使用的时候最好再加个字符串

    第二种:openssl rand -base64 10

    [root@web-01 /server/scripts]# openssl rand -base64 10(最后面是长度)
    yz+FH2zUNMlVnw==
    [root@web-01 /server/scripts]# openssl rand -base64 100
    wkfkVmliczOgoLl0z/m5S/7InZ8+4AzdHmR6t6hhE80oghRY46598L+no+HtDcHD
    HyvQYnBWi6nQ0GbsjafyWZps7y6JpMEA6JOwQ+HlIOICXT7YLCcI9mQa6FUE+vHR
    OcxHog==

    第三种:date

    [root@web-01 /server/scripts]# date +%N%s()
    3057895901553937109

    第四种:head /dev/urandom |cksum

    [root@web-01 /server/scripts]# head /dev/urandom |cksum
    1831677657 1682

    第五种:uuidgen

    [root@web-01 /server/scripts]# uuidgen
    218780c9-ee6f-41dc-9058-a9a3a717cde1

    第六种:cat /proc/sys/kernel/random/uuid

    [root@web-01 /server/scripts]# cat /proc/sys/kernel/random/uuid
    542f71d9-b240-4891-b61d-632083ecf6be

    第七种:expect的mkpasswd

    [root@web-01 /server/scripts]# mkpasswd(yum install expect -y)
    k3WlhJ|e7
    
    [root@web-01 /server/scripts]# mkpasswd -l 20 -d 1 -c 2 (-l长度、-d数字、-c小写字母、-C大写字母、-s特殊字符)
    nebiv;bnZi6vjluvczgP

    本次使用$RANDOM,前面加字符串,md5加密 ,将数字替换为字母,截取第2-第11个,共10位

    [root@web-01 ~]# echo "PIZZA$RANDOM" |md5sum|tr "0-9" "a-z"|cut -c 2-11
    befdbcdaee

    2、for循环创建

    path=/pizza
    [ -d $path ] || mkdir $path
    for n in {1..10}
    do
        random=`echo "PIZZA$RANDOM" |md5sum|tr "0-9" "a-z"|cut -c 2-11`
        touch $path/${random}_pizza.html
    done

    2、批量改名

    将题1中的pizza都改成linux(最好用for循环实现,并且扩展名html全部改成大写)

    思路分析:

    1、先改一个

    2、for循环

    name=linux
    path=/pizza/
    cd $path
    for file in `ls *.html`
    do
        mv $file `echo ${file/pizza.html/linux.HTML}`                                   
    done

    3、方法二--用一条命令

    [root@web-01 /pizza]# ls *.HTML |awk -F 'linux.HTML' '{print "mv",$0,$1"pizza.html"}' |bash

    4、方法三--专业的rename

    [root@web-01 /pizza]# rename "pizza.html" "linux.HTML" *.html

    3、批量创建特殊需求要求用户案例

    批量创建10个系统账号 pizza01-pizza10 并设置密码(密码是随机数,要求字符和数字等混合)

    思路分析:

    1、创建10个账号

    第一种:echo pizza{01..10}

    第二种:seq -w 10

    2、随机密码,题1已经讲了很多,这次用openssl rand -base64 100

    3、创建用户的命令

    第一种:

    useradd  pizza01

    echo   密码 | passwd  --stdin

    第二种:chpasswd命令

    格式要符合下面的形式

    pizza01:passwd

    pizza02:passwd

    4、for循环

    for n in {01..10}
    do
        passwd=`openssl rand -base64 10`
        useradd pizza$n
        echo $passwd |passwd --stdin pizza$n
        echo -e "pizza$n	$passwb" >> /pizza/user.list                                  
    done

    或者

    for n in {01..10}
    do
        passwd=`openssl rand -base64 10`
        useradd pizza$n
        echo "pizza$n:$passwd" >> /pizza/pass.log
    done
    chpasswd < /pizza/pass.log 

    5、优化,搞的专业一点

    for n in {1..10}
    do
        pass=`openssl rand -base64 10`                                                  
        if `grep "pizza$n" /etc/passwd &>/dev/null`    # 判断是不是存在
        then
            useradd pizza$n &&     # &&设置强逻辑关系
                echo $pass|passwd --stdin pizza$n &&
                echo -e "pizza$n	$pass" >> /pizza/user.log
        else
            echo "pizza$n is exist."
        fi
    done

    优化:

    for n in {1..10}
    do
        pass=`openssl rand -base64 10`
        if [ `grep -w "pizza$n" /etc/passwd|wc -l` -eq 0 ]
        then
            useradd pizza$n &&
                echo $pass|passwd --stdin pizza$n &&
                echo -e "pizza$n	$pass" >> /pizza/user.log
            echo "adduser successful"
    
        else
            echo "pizza$n is exist."
        fi
    done  

    优化:

    . /etc/init.d/functions
    for n in {1..10}
    do
        pass=`openssl rand -base64 10`
        if [ `grep -w "pizza$n" /etc/passwd|wc -l` -eq 0 ]
        then
            useradd pizza$n &&
                echo $pass|passwd --stdin pizza$n &>/dev/null &&
                echo -e "pizza$n	$pass" >> /pizza/user.log
            action "adduser successful" /bin/true
    
        else
            action "pizza$n is exist." /bin/false
        fi
    done 

    优化:

    . /etc/init.d/functions
    if [ $UID -ne 0 ]
    then
        echo "必须用root执行本脚本"
        exit 1
    fi
    
    for n in {1..10}
    do
        pass=`openssl rand -base64 10`
        if [ `grep -w "pizza$n" /etc/passwd|wc -l` -eq 0 ]
        then
            useradd pizza$n &&
                echo $pass|passwd --stdin pizza$n &>/dev/null &&
                echo -e "pizza$n	$pass" >> /pizza/user.log
            action "adduser successful" /bin/true
    
        else
            action "pizza$n is exist." /bin/false
        fi
    done 

    6、不用for循环的实现

    批量创建10个用户stu01-stu10,并且设置随机8位密码,要求不能用shell的循环(例如:for,while等),只能用linux命令及管道实现。
    
    方法1:
    [root@oldboy /]# echo stu{01..10}|tr " " "
    "|sed -r 's#(.*)#useradd 1 ; pass=$((RANDOM+10000000)); echo "$pass"|passwd --stdin 1; echo -e "1 	 `echo "$pass"`">>/tmp/oldboy.log#g'|bash
     
    上述命令实际就是再拼N条下面的命令的组合,举一条命令stu01用户的过程拆解如下:
    useradd stu01 ; 
    pass=$((RANDOM+10000000)); 
    echo "$pass"|passwd --stdin stu01; 
    echo -e "stu01        `echo "$pass"`">>/tmp/oldboy.log
    特别说明:如果用shell循环结构会更简单,之所以限制使用循环的目的是锻炼学生的基础命令运用
    能力,学到现在还没学到SHELL循环课程呢
    
    方法2:来自酒醉饭饱
    echo stu{11..12}|xargs -n1 useradd ;echo stu{11..12}:`cat /dev/urandom|tr -dc 0-9|fold -w8|head -1`|xargs -n1|tee -a pass.txt|chpasswd 
     
    方法3:来自D调的华丽
    有个参数写错了, cut时应该取第二个字段 应是 -f2  结果应该是这样: echo stu{21..30} | tr ' ' '
    ' | sed -e 's/^/useradd /' -e 's/(stu[0-9]{2})$/1 && echo "1:`echo $[$RANDOM**3] | cut -c1-8`" | tee -a userInfo.txt | cut -d: -f2 | passwd --stdin 1/' | bash
    功能: 创建10个用户 分别是 stu21-stu30 其密码是用随机数变量RANDOM生成,均保存至 userInfo.txt中,格式: username:passwd   这个写的不算好  如果有更好的一定要分享哦!  上面的随机数 我之前是用日期生成的,是不对的,因为有可能会有重复现象,所以我后来干脆用RANDOM**3取其前8位,可确保唯一性
    
    方法4:来源 freeandeasy
    echo stu{01..10} |tr ' ' '
    '|sed -rn 's@^(.*)$@useradd 1 ; echo $RANDOM|md5sum|cut -c 1-8 >/data/1;cat /data/1|passwd --stdin 1@gp'|bash

    4、扫描网络内存活主机

    写一个Shell脚本,判断10.0.0.0/24网络里面,当前在线的IP有哪些

    思路分析

    1、判断主机存活

    ping  c 2 i 1 w 3 10.0.0.7
    nmap  -sP 10.0.0.0/24

    2、搞起来

    第一种:ping

    for n in {1..254}
    do
        # 将整个执行用大括号括起来 加 &,进行批量ping,原理就是放到后台执行
        {
        if `ping -c 1 -w 3 10.0.0.$n &>/dev/null`
        then
            echo "10.0.0.$n is up"
        else
            echo "10.0.0.$n is down"
        fi
    } &
    done

    # 没有进行过滤,所以输出很多,可以优化一下
    第二种:使用nmap命令行就可以

    [root@web-01 /server/scripts]# nmap -sP 10.0.0.0/24 |awk '/Nmap scan report for/{print $NF}'

    5、MySQL分库备份

    实现对MySQL数据库进行分库备份,用脚本实现

    为什么要进行分库备份呢,因为,如果在以后需要备份一个小库,就会很麻烦

    常规方法:

    mysqldump -B userinfo click test | gzip >bak.sql.gz

    分库备份:

    mysqldump -B userinfo | gzip >bak.sql.gz
    mysqldump -B click | gzip >bak.sql.gz
    mysqldump -B test | gzip >bak.sql.gz

    执行命令获取库名

    mysql -uroot -ppizza123 -e "show databases"   |  grep -v _schema | sed 1d

    把密码放到配置文件中

    cat  /etc/my.cnf
    [client]
    user = root
    passwd = pizza123

    做完这一步就不用在脚本中添加-u和-p参数

    备份脚本

    path=/backup
    mysql="mysql -uroot -ppizza123"
    mysqldump="mysqldump -uroot -ppizza123"
    [ -d $path ] || mkdir $path
    for dbname in `$mysql -e "show databases;" 2>/dev/null|grep -v _schema | sed 1d`
    do
        # 这个命令还有很多参数没有写
        $mysqldump -B $dbname |gzip >/backup/${dbname}_$(date +%F).sql.gz
    done  

    6、MySQL分库、分表备份

    常规备份

    mysqldump  pizza  test  test1 | gzip > bak.sql.gz
    
    pizza是库名、test和test1是表名

    分库、分表备份

    mysqldump  -B pizza  | gzip >bak.sql.gz
    mysqldump pizza test1
    mysqldump pizza test2
    mysqldump pizza test3

    脚本编码

    path=/backup
    mysql="mysql -uroot -ppizza123"
    mysqldump="mysqldump -uroot -ppizza123"
    [ -d $path ] || mkdir $path
    for tname in `$mysql -e "show tables from $dbname;" 2>/dev/null|sed 1d`
        do
            if [ "$dbname" = "mysql" ]
            then
                # 这个命令还有很多参数没有写
                $mysqldump --skip-lock-tables $dbname $tname |gzip >$path/${dbname}-$tname_$(date +%F).sql.gz 2>/dev/null
            else
                $mysqldump $dbname $tname |gzip >$path/${dbname}-$tname_$(date +%F).sql.gz 2>/dev/null
            fi
        done
    done

    7、SSH服务批量分发与管理服务

    确保主机能用root登陆

    vim /etc/ssh/sshd_config 字段 PermitRootLogin 为 yes

    建立密钥对

    ssh-keygen

    发送公钥到其他服务器

    ssh-copy-id  -i  id_rsa.pub  10.0.0.8

    可能会很慢,调整其他机器的配置,让操作快一些

    useDNS no
    
    GSSAPIAuthentication no

    重启服务,继续

    ssh-copy-id  -i  id_rsa.pub  10.0.0.8
    
    ssh-copy-id  -i  id_rsa.pub  10.0.0.9

    写一个链接主机并可以执行命令的脚本

     vim ssh_.sh

    if [ $# -ne 1 ]
    then
      echo "usage:$0  cmd"
      exit 1 
    fi
    for  n  in 8 9
    do
      echo  "-----10.0.0.$n------"
      ssh 10.0.0.$n  $1
    done

    执行脚本

    bash  ssh_.sh  "free -m"

    编写分发脚本

    . /etc/init.d/functions
    if [ $# -ne 2]
    then
        echo "usage:$0 localdir remotedir"
        exit 1
    fi
    
    for n in 8 9
    do
        scp -rp $1 10.0.0.$n:$2 &>/dev/null
        if [ $? -eq 0 ]
        then
            action "10.0.0.$n sucessful" /bin/true
        else
            actin "10.0.0.$n fail" /bin/false
        fi
    done

    8、破解RANDOM随机数案例

     已知下面这些字符串是通过RANDOM随机变量 md5sum 后,再截取一部分连续字符串的结果,亲个破解这些字符串对用的使用md5sum 处理前的RANDOM对应的数字

    21023299

    00205d1c

    a3da1677

    1f6d12dd

    890684b

    解答:

    1、分析

    RANDOM的随机范围是0-32767 。

    显现需要把范围内的数字都加密,输出到md5.log中

    2、比较

    grep “890684b” md5.log | wc -l

    3、编码实现

    array=(
    21023299
    00205d1c
    a3da1677
    1f6d12dd
    890684b
    )
    md5(){
        for n in {0..32767}
        do
            echo -e "$n	`echo $n|md5sum`" >> /pizza/md5.log
        done
    
    }
    crack_num(){
        for num in ${array[*]}
        do
            find=`grep $num /pizza/md5.log`
            if [ `echo $find|wc -l` -eq 1 ]
            then
                echo $find
            fi
        done
    }
    main(){
        md5
        crack_num
    }
    main 

    第二种方法:egrep实现

    [root@web-01 /server/scripts]# array=(
    > 21023299
    > 00205d1c
    > a3da1677
    > 1f6d12dd
    > 890684b
    > )
    
    [root@web-01 /server/scripts]# cmd=`echo ${array[*]}|tr " " "|"`
    
    [root@web-01 /server/scripts]# egrep "$cmd" /pizza/md5.log 

    修改第一版

    array=(
    21023299
    00205d1c
    a3da1677
    1f6d12dd
    890684b
    )
    md5(){
        for n in {0..32767}
        do
            echo -e "$n	`echo $n|md5sum`" > /pizza/md5.log &
        done
    
    }
    crack_num(){
        cmd=`echo ${array[*]}|tr " " "|"`
        egrep "$cmd" /pizza/md5.log
    }
    
    main(){
        md5
        crack_num
    }
    main 

    利用time命令对比两个版本的时间

    time sh 8_random_crack.sh

    9、检查多个网站地址是否正常

    要求

    1、使用shell数组方法,检测策略精良模拟用户访问

    2、每10秒种做一次所有的检测,无法访问的输出报警

    3、待检测网址如下

    https://www.cnblogs.com/yxiaodao/

    https://www.baidu.com/

    检测工具:

    url

    curl

    wget

    脚本编码

    . /etc/init.d/functions
    url=(
    https://www.cnblogs.com/yxiaodao/
    https://www.baidu.com/
    )
    check_url(){
        wget -t 2 -T 5 -o /dev/null -q $1
        if [ $? -eq 0 ]
        then
            action "$1 is ok" /bin/true
        else
            action "$1 is lost" /bin/false
        fi
    }
    
    DealUrl(){
        for url in ${url[*]}
        do
            check_url $url
        done
    }
    
    main(){
        while true
        do
            DealUrl
            sleep 10
        done
    
    }
    main 

    修改题目,不用数组,将网址放在文件中,做如下修改

    DealUrl(){
        while read line
        do
            check_url $line                                                                                                
        done < ./pizza/url.log
    }

    有一个问题,在我们操作完后,会产生大量的网页文件,因为我们的命令将网页下载了

    需要在命令中添加参数 -- spider

    10、利用Shell编程分析Web日志解决Dos攻击生产案例

    DOS  Deny  of  Service

    DDOS  分布式dos攻击

    请根据web日志或者网络连接数,监控当某个IP并发连接数或者短时间内PV达到100(根据实际情况设定),即调用防火墙命令封掉对应的IP。

    防火墙命令:iptables  -l  INPUT  -s  IP地址  -j  DROP

    分析:

    1、web日志或者网络连接数

      日志文件,netstat  -an | grep  -i  est,排序去重

    2、判断PV 或者连接数大于100 ,取出IP ,封IP

    IP 在日志的第一列,取到IP---->排序---->统计数量----> 按数量从大到小排序

    [root@web-01 /server/scripts]# awk '{print $1}' access_2010-12-8.log |sort|uniq -c|sort -rn
         35 59.33.26.105
         23 123.122.65.226
          8 124.115.4.18

    也能通过awk的数组来完成

    [root@web-01 /server/scripts]# awk '{S[$1]++}END{for(key in S) print S[key],key}' access_2010-12-8.log |sort -rn
    35 59.33.26.105
    23 123.122.65.226
    8 124.115.4.18

    编码脚本

      9 awk '{S[$1]++}END{for(key in S) print S[key],key}' access_2010-12-8.log |sort -rn > /pizza/ip.log
     10 while read line
     11 do
     12     ip=`echo $line|awk '{print $2}'`
     13     count=`echo $line|awk '{print $1}'`                                                                            
     14     if [ $count -gt 30 -a `grep $ip /pizza/drop.log|wc -l` -lt 1  ]
     15     then
     16         iptables -I INPUT -s $ip -j DROP &&
     17             echo "$ip" >>/pizza/drop.log
     18     else
     19         echo "$ip" >>/pizza/accept.log
     20     fi
     21 done</pizza/ip.log

    本次采用了读取drop.log日志的方法,也可以采用查看 iptables -nL的方法

    10、利用Shell编程分析Linux服务器网络链接数解决DOS攻击生产案例实践

    还是上一个题,上面的题监控的是web日志,本次是监控网络链接数实现

    命令:ESTABLISHED 正在建立的链接状态

    [root@web-01 /server/scripts]# netstat -an |grep -i ESTABLISHED
    Active Internet connections (servers and established)
    tcp        0      0 172.17.214.84:47778     107.175.240.135:2222    ESTABLISHED
    tcp        0      0 172.17.214.84:34820     100.100.30.25:80        ESTABLISHED
    tcp        0     52 172.17.214.84:22        163.125.30.51:37793     ESTABLISHED
    Active UNIX domain sockets (servers and established)

    获取外部地址,统计,排序(为方便,将命令的输出到了日志)

    [root@web-01 ~]# awk '/ESTAB/{print $0}' netstat.log|awk -F "[ :]+" '{print $(NF-3)}'|sort|uniq -c|sort -rn

    高级写法,通过awk数组

    [root@web-01 ~]# awk -F "[ :]+" '/ESTAB/{S[$(NF-3)]++}END{for(k in S) print S[k],k}' netstat.log | sort -rn |head

    不用日志,用netstat -an

    [root@web-01 ~]# netstat -an|awk -F "[ :]+" '/ESTAB/{S[$(NF-2)]++}END{for(k in S) print S[k],k}' | head
    1 163.125.30.51
    1 100.100.30.25
    1 107.175.240.135
    因数据差异,具体命令中参数还要自己调

    脚本编写,只需修改前一个脚本的第一行获取ip的命令即可

    netstat -an|awk -F "[ :]+" '/ESTAB/{S[$(NF-2)]++}END{for(k in S) print S[k],k}'|head >/pizza/ip.log
    
    while read line 
    do
        ip=`echo $line|awk '{print $2}'`
        count=`echo $line|awk '{print $1}'`
        if [ $count -gt 30 -a `grep $ip /pizza/drop.log|wc -l` -lt 1  ]
        then
            iptables -I INPUT -s $ip -j DROP &&
                echo "$ip" >>/pizza/drop.log
        else
            echo "$ip" >>/pizza/accept.log
        fi
    done</pizza/ip.log 
    在实际工作中,可以设置定时任务,每3分钟执行一次,每天晚上0点取消

    11、开发MySQL服务启动停止脚本

    要求:用函数,case语句,if语句等实现 /etc/init.d/mysqld  {start | stop | restart} 命令

    分析:

    1、启动

     mysqld_safe  --user=mysql &

    2、停止

    mysqladmin  -uroot  -ppasswd  shutdown

    killall,pkill(参考之前写的rsync 第九章-case结构条件句)

    3、脚本编码

    看一下mysql的pid的文件位置

    # 定义锁文件
    lockfile=/var/lock/subsys/mysqld
    # 定义变量,指定mysqld的的pid,是需要自己mysql的conf中去创建
    mysql_pid_file_path=/application/mysql/data/`uname -n.pid`
    . /etc/init.d/functions
    start(){
        mysql_safe --user=mysql &>/dev/null &
        retval=$?
        if [ $retval -eq 0 ]
        then
            action  "mysql startup ok" /bin/true
            touch $lockfile
            return $retval
        else
            action "mysql startup fail" /bin/false
            return $retval
        fi
    }
    stop(){
        if test -s "$mysql_pid_file_path"
        then
            mysql_pid=`cat $mysql_pid_file_path`
            if (kill -0 $mysql_pid &>/dev/null)
            then
                # 为了在重复停止操作的时候,不提示,将其扔到黑洞
                kill $mysql_pi
                retval=$?
                if [ $? -eq 0 ]
                then
                    action "mysql stop ok" /bin/true
                    rm -f $lockfile
                    return $retval
                else
                    action "mysql stop fail" /bin/false
                    return $retval
                fi
            else
                echo "mysqld_process is not exit."
                return 2
            fi
        else
            echo "$mysqld_pid_file_path is not exist,or mysqld does not startup"
        fi
    
    }
    restart(){
        killall mysql && sleep 1 && mysql --deamon
        retval=$?
        if [ $? -eq 0 ]
        then
            action "mysql restart ok" /bin/true
            return $retval
        else
            action "mysql restart fail" /bin/false
            return $retval
        fi
        }
    case "$1" in
        start)
            start
            # 我了向外传值
            retval=$?
            ;;
        stop)
            stop
            retval=$?
            ;;
        restart)
            stop
            sleep 2
            start
            retval=$?
            ;;
    
        *)
            echo "usage:$0 {start|stop|restart}"
            exit 1
    esac
    exit $retval

    出现问题,启动了,但是没有pid文件

    解决:

    1、先使用系统的命令开启。

    2、通过查看ps -ef |grep mysql 查看 启动参数

    添加启动参数 --pid-file=$mysql_pid_file_path

    问题:进不去mysql

    添加参数--datedir=/application/mysql/data

    问题:启动的过程很快,执行脚本后,启动成功,但是没有发现进程和pid,无法进入

    1、查看mysql日志

    cat /application/mysql/data/web01.err

    2、发现使用脚本中的命令,手动也起不来

    3、使用系统执行后的启动命令

    /bin/sh  /application/masql/bin/mysqld_safe  --datedir=/application/mysql/data  --pid-file=$mysql_pid_file_path

    脚本一定要现在命令行测试成功,再写入脚本中

    最后一个任务

    拷贝到/etc/init.d中 ,变成chkconfig 可已使用的脚本

    12、按单词去重排序

    In the world of hackers, the kind of answers you get to your technical questions depends as much on the way you ask the questions as on the difficulty of developing the answer. This guide will teach you how to ask questions in a way more likely to get you a satisfactory answer.
    Now that use of open source has become widespread, you can often get as good answers from other, more experienced users as from hackers. This is a Good Thing; users tend to be just a little bit more tolerant of the kind of failures newbies often have. Still, treating experienced users like hackers in the ways we recommend here will generally be the most effective way to get useful answers out of them, too.
    The first thing to understand is that hackers actually like hard problems and good, thought-provoking questions about them. If we didn't, we wouldn't be here. If you give us an interesting question to chew on we'll be grateful to you; good questions are a stimulus and a gift. Good questions help us develop our understanding, and often reveal problems we might not have noticed or thought about otherwise. Among hackers, “Good question!” is a strong and sincere compliment.
    Despite this, hackers have a reputation for meeting simple questions with what looks like hostility or arrogance. It sometimes looks like we're reflexively rude to newbies and the ignorant. But this isn't really true.
    What we are, unapologetically, is hostile to people who seem to be unwilling to think or to do their own homework before asking questions. People like that are time sinks — they take without giving back, and they waste time we could have spent on another question more interesting and another person more worthy of an answer. We call people like this “losers” (and for historical reasons we sometimes spell it “lusers”).
    We realize that there are many people who just want to use the software we write, and who have no interest in learning technical details. For most people, a computer is merely a tool, a means to an end; they have more important things to do and lives to live. We acknowledge that, and don't expect everyone to take an interest in the technical matters that fascinate us. Nevertheless, our style of answering questions is tuned for people who do take such an interest and are willing to be active participants in problem-solving. That's not going to change. Nor should it; if it did, we would become less effective at the things we do best.
    We're (largely) volunteers. We take time out of busy lives to answer questions, and at times we're overwhelmed with them. So we filter ruthlessly. In particular, we throw away questions from people who appear to be losers in order to spend our question-answering time more efficiently, on winners.
    If you find this attitude obnoxious, condescending, or arrogant, check your assumptions. We're not asking you to genuflect to us — in fact, most of us would love nothing more than to deal with you as an equal and welcome you into our culture, if you put in the effort required to make that possible. But it's simply not efficient for us to try to help people who are not willing to help themselves. It's OK to be ignorant; it's not OK to play stupid.
    So, while it isn't necessary to already be technically competent to get attention from us, it is necessary to demonstrate the kind of attitude that leads to competence — alert, thoughtful, observant, willing to be an active partner in developing a solution. If you can't live with this sort of discrimination, we suggest you pay somebody for a commercial support contract instead of asking hackers to personally donate help to you.
    If you decide to come to us for help, you don't want to be one of the losers. You don't want to seem like one, either. The best way to get a rapid and responsive answer is to ask it like a person with smarts, confidence, and clues who just happens to need help on one particular problem.

    按单词出现的频率降序排序

    1、把空格和符号都转换成空格--排序--统计--排序

    2、命令

    [root@web-01 /server/scripts]# cat english.txt |tr "“”! ,.)( " "
    " |sort|uniq -c |sort -rn

    方法二:

    [root@web-01 /server/scripts]# cat english.txt |tr "“”! ,.)( " "
    " |awk '{S[$1]++}END{for(k in S) print S[k],k}'|sort -rn

    方法三:

    cat english.txt |xargs -n1

    12、按字母去重排序

    按字母出现的频率降序排序

    1、使用 grep -o ‘.’  匹配任意 之后,会挨个输出 或者 grep -o "[^ ]" 

    grep -o "[^ ,.()]" english.txt |awk '{S[$1]++}END{for(k in S) print S[k],k}'|sort -rn

    2、awk可以用空做分隔符

    sed 's#[ ,.]##g' english.txt|awk -F "" '{for(i=0;i<NF;i++)S[$i]++}END{for(k in S) print S[k],k}' |sort -rn

    暂时没有设计出过滤换换行符

    13、按单词去重排序高级方案

     基于上面的awk统计单词

    awk -F "[ ,.]" '{for(i=1;i<NF;i++)S[$i]++}END{for(k in S) print S[k],k}' english.txt |sort -rn
  • 相关阅读:
    ComboBox中Tag的使用,转换为Enum类型
    datagridview定时分页(翻页)
    DataGridView导出excel/xml
    Log4net学习笔记及部分使用方法
    C#中Abstract 与 Virtual
    The project type is not supported by this installation?
    全国哀悼日 网站变灰装(附代码)
    设计出好的对象模式
    不仅拥有XmlDocument一样简单的XML操作方法,并且实现数据文件安全存储功能——XmlEDocument
    Remoting模仿QQ实现客户端,服务器端聊天功能
  • 原文地址:https://www.cnblogs.com/bubu99/p/12275395.html
Copyright © 2011-2022 走看看