shell编程由浅入深

1.  学好shell编程的知识储备

    1.  linux系统命令应用

    2.  vi/vim编辑器的熟练使用。SSH客户端软件的设置。

    3.  基础的服务,系统服务ntp crond,网络服务nfs rsync inotify sersync ssh lamp lnmp

2.  Shell脚本简介

     1.  什么是shell?

        Shell是一个命令解释器,是linux/unix操作系统的最外层,负责直接与用户对话,把用户输入的命令解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕给用户。

        

     2.  通过SSH登录到linux系统,就要经过shell解释器,默认是bash.

     3.  什么是shell脚本?

        当命令或者语句不在命令行执行,而是通过一个程序文件执行时,该程序或文件就被成为shell脚本或者shell程序,shell程序类似DOS系统下的批处理程序。

     4.  简单的例子

[root@yangjianboinbeijing day1]# cat del_messages.sh 
cd /var/log
cat /dev/null > messages
echo "messages is null!"

        这个脚本的缺点:缺乏基本的逻辑判断,会导致前面的命令出错后,后面的命令无法正确执行。

        上面脚本的升级版:

#!/bin/bash
#清除messages日志脚本,版本2--yangjianbo
LOG_DIR=/var/log
ROOT_UID=0
if [ "$UID" -ne "$ROOT_UID" ]
then
  echo "You must be root."
  exit 1
fi
cd $LOG_DIR || {
   echo "please change to /var/log/">&2
   exit 1
}
cat /dev/null > messages && echo "messages is cleaned up"
exit 0

3.  Shell程序在运维工作中的作用和地位

    Shell脚本很擅长处理纯文本类型的数据,而linux中几乎所有的配置文件和日志文件都是纯文本类型的文件。

    贴近系统层的操作,shell是最佳的。shell的伙伴(2000个linux命令,awk,sed,grep)

   

4.  Shell脚本语言的种类

     1.  主要分为两大类

               Bourne shell

               C shell

     2.  高级运维或者开发型运维常用的脚本语言

             php

             perl

             python

5.  常用操作系统的默认shell

     Linux  bash

     Solaris和FreeBSD    sh

      AIX  ksh

    HP-UX  sh

    如何查看系统的默认shell

      echo $SHELL或者cat /etc/passwd查看用户登录的shell

    查看系统的shell版本

      bash --version

6.  建立一个规范的shell脚本

    1.  shell脚本组成:Unix/linux命令 bash shell命令 注释 程序结构控制语句

       第一行指出是由哪个解释器来执行。

       #!/bin/bash或者#!/bin/sh

       #!/bin/awk

       #!/bin/sed

      注释:使用的是#

7.   Shell脚本执行的多种方法及重要区别

   1.  方法

      第一种: 当脚本本身没有执行权限,使用bash 脚本或者sh 脚本。文件权限没有x

          /bin/bash 1.sh

          bash 1.sh

          sh 1.sh

      第二种:需要脚本有权限,使用./脚本名执行或者全路径。文件权限必须得有x

          ./1.sh

      第三种:source script-name或者.script-name

      第三种与第一种和第二种的区别:

      举例来说明:

面试题:
cat test.sh
user=`whoami`
sh test.sh
echo $user
结果是什么?
结果为空,这是为什么呢?
当使用第一种方法和第二种方法执行sh文件,系统会给一个新的bash执行让我们执行sh里面的命令,因此变量都是子进程的bash中执行的。当sh执行完毕后,子进程bash内的所有数据被删除,因此为空。
但是使用第三种方法就不一样了,因为是在自己的进程中执行sh,不会产生子进程,所以不会为空。

8.  Shell脚本开发规范和习惯

    1.  开头指定脚本解释器

      #!/bin/bash或者#!/bin/sh

    2.  开头加版本版权功能作者等信息

    3.  脚本中不用中文注释

    4.  脚本以.sh为扩展名

    5.  代码书写的优秀习惯

      成对出现的符号一次性写完。

      for循环要一次写完。

      if语句要一次性写完。

      通过手工缩进让代码更容易读。让代码有层次感。

9.  Shell变量基础与深入

    1.  分类:环境变量和局部变量

       环境变量也称为全局变量,可以在创建它们的shell及其派生出来的任意子进程shell中使用。

       局部变量只能在它们自己的shell中或脚本中使用。

    2.  环境变量

       用于定义shell的运行环境,保证shell命令的正确执行,shell通过环境变量来确定登陆用户名,命令路径,终端类型,登录目录等。

       环境变量可以在命令行设置,但是注销就消失,所以需要在用户的家目录.bash_profile或者全局配置/etc/profile或者/etc/profile.d/中定义。

       环境变量均为大写,必须用export导出。

    3.  环境变量读取配置文件,分为两种情况。

      1.  login shell

          1.  直接登录服务器,输入账号和密码

          2.  通过ssh登录服务器,输入账号和密码

          3.  su - username          

      2.  non-login shell

          1.  su username

          2.  图形界面下打开的终端

          3.  执行脚本

          4.  通过ssh免密钥的方式,直接执行命令。

              ssh java@192.168.1.100 'echo $TESTZZ'  注意在命令行使用单引号,脚本使用双引号

      3.  配置文件分类

          1.  按生效范围划分

              1.  全局

                  /etc/profile

                  /etc/bashrc

                  /etc/profile.d/*.sh

              2.  个人

                  ~/.bash_profile

                  ~/.bashrc

          2.  按功能划分

              1.  profile类  为交互式登录的shell提供配置

                  用途:定义环境变量

                     运行命令或脚本

                  全局:/etc/profile

                  个人:~/.bash_profile

              2.  bashrc类  为非交互式登录的shell提供配置

                  用途:  定义命令别名

                       定义本地变量

                  全局:/etc/bashrc

                  个人:~/.bashrc

      4.  两种登录方式配置文件引用

          1.  交互式登录

              /etc/profile-->/etc/profile.d/*.sh-->~/.bash_profile-->~/.bashrc-->/etc/bashrc

          2.  非交互式登录

              ~/.bashrc-->/etc/bashrc-->/etc/profile.d/*.sh

    4.  查看环境变量

      env: 列出所有的环境变量

      set: 查看所有变量(含环境变量与自定义变量)

      export: 自定义变量转成环境变量

10.  显示与取消环境变量

     1.  显示echo $LANG

     2.  env命令

     3.  set命令 

     4.  取消环境变量

       unset 变量名

11.  局部变量

     1.  本地变量

       1.  定义:本地变量在用户当前shell生存期的脚本中使用。退出或者进入另外一个进程,就会失效。

       2.  变量定义:

         例子1:

[root@yangjianboinbeijing day1]# a=192.168.1.2
[root@yangjianboinbeijing day1]# b='192.168.1.2'
[root@yangjianboinbeijing day1]# c="192.168.1.2"
[root@yangjianboinbeijing day1]# echo "a=$a" "b=$b" "c=${c}"
a=192.168.1.2 b=192.168.1.2 c=192.168.1.2

         例子2:

[root@yangjianboinbeijing day1]# a=192.168.1.2-$a
[root@yangjianboinbeijing day1]# echo "a=$a"
a=192.168.1.2-192.168.1.2
[root@yangjianboinbeijing day1]# b='192.168.1.2-$a'
[root@yangjianboinbeijing day1]# echo "b=$b"
b=192.168.1.2-$a
[root@yangjianboinbeijing day1]# c="192.168.1.2-$a"
[root@yangjianboinbeijing day1]# echo "c=${c}"
c=192.168.1.2-192.168.1.2-192.168.1.2
为什么是这样的结果呢?
因为单引号内的特殊字符仅为一般字符(单引号里面是什么就是什么),而双引号内的特殊字符保留原来的特性,例如$符号。
推荐字符串定义使用双引号。
这种方式在awk调用shell变量刚好相反。单引号可以保留原字符的特性,但是双引号里面是什么就是什么。

         老男孩运维博客:http://oldboy.blog.51cto.com/2561410/760192

       3.  变量的命名规范

         1.  都使用大写

         2.  不能以数字开头

         3.  等号两边不能直接有空格,如果需要使用空格,需要加引号。

       4.  把命令作为变量

         需要使用反引号。

         也可以使用$()

批量创建目录
mkdir `seq 5`
mkdir $(echo {a..e})
mkdir {o..w}

      5.  关于awk引用shell变量的使用情况

          

12.  Shell的特殊变量

       1.  位置变量

         $0  获取当前执行的shell脚本的文件名,包括路径。

[root@yangjianboinbeijing day1]# cat 0.sh 
#!/bin/bash
echo $0
[root@yangjianboinbeijing day1]# sh 0.sh     #取文件名
0.sh
[root@yangjianboinbeijing day1]# sh /server/scripts/day1/0.sh    #取完整路径和文件名
/server/scripts/day1/0.sh

[root@yangjianboinbeijing day1]# cat 0.sh 
#!/bin/bash
dirname $0        从$0中取路径
basename $0       从$0中取文件名
[root@yangjianboinbeijing day1]# sh /server/scripts/day1/0.sh 
/server/scripts/day1
0.sh

 

想单独获得路径和文件名

dirname /server/scripts/0.sh

basename /server/scripts/0.sh

       $n  获取当前执行的shell脚本的第n个参数值,当n大于9,用大括号括起来${10}。

[root@yangjianboinbeijing day1]# cat n.sh 
#!/bin/bash
echo $1 $2 $3 ${10}
[root@yangjianboinbeijing day1]# bash n.sh {1..11}
1 2 3 10
[root@yangjianboinbeijing day1]# bash n.sh `seq 11`
1 2 3 10

       $#  获取当前执行的shell脚本的传入的参数个数。

[root@yangjianboinbeijing day1]# cat n.sh 
#!/bin/bash
echo $1 $2 $3 ${10}
echo $#
[root@yangjianboinbeijing day1]# sh n.sh 1 2 3 4
1 2 3
4

[root@mysql-37 scripts]# /bin/bash canshu.sh {a..z}  传入的参数个数为26,但实际接收的参数个数位为4
a b c d
26  

用于判断shell脚本参数个数的时候。

实战例子:

[root@yangjianboinbeijing day1]# cat bijiao.sh 
#!/bin/bash
if [ $# -ne 3 ];then
  echo "please input 3 args!"
else
  echo "input OK"
fi
[root@yangjianboinbeijing day1]# sh bijiao.sh 
please input 3 args!
[root@yangjianboinbeijing day1]# sh bijiao.sh 1
please input 3 args!
[root@yangjianboinbeijing day1]# sh bijiao.sh 1 2
please input 3 args!
[root@yangjianboinbeijing day1]# sh bijiao.sh 1 2 3
input OK
[root@yangjianboinbeijing day1]# sh bijiao.sh 1 2 3 4
please input 3 args!

      $*  获取当前shell传入的所有的参数

#!/bin/bash
user=`whoami`
echo $*

[zhangshaohua1510@mysql-37 scripts]$ /bin/bash test.sh 1 2 3
1 2 3

      

      $@  获取当前shell所有传参的参数

#!/bin/bash
user=`whoami`
echo $@

[zhangshaohua1510@mysql-37 scripts]$ /bin/bash test.sh 1 2 3
1 2 3

      $*与$@的区别在于,如果不加双引号,那么它们的结果是一样;如果加上双引号,$*输出的结果是"$1 $2 $3",而$@输出的结果是"$1" ,"$2","$3"。

      不带引号的结果:  

#!/bin/bash
echo $*
for i in $*;
do 
  echo $i
done
echo $@
for i in $@;
do 
  echo $i
done

sh test.sh "I am"  yang jian bo
不带双引号的时候,两个输出的结果是一样的。
I am yang jian bo   这是$*
I  
am
yang
jian
bo
I am yang jian bo   这是$@
I
am
yang
jian
bo

      带双引号的结果:

#!/bin/bash
echo $*
for i in "$*";
do
  echo $i
done
echo $@
for i in "$@";
do
  echo $i
done

sh test.sh "I am"  yang jian bo
带双引号的时候,$*与$@的结果完全不一样。

I am yang jian bo  这是$*
I am yang jian bo  $*带引号以后,把$1 $2 $3当做一个整体,输出了。
I am yang jian bo  这是$@
I am        $@带引号以后,把参数一个一个输出了。
yang
jian
bo

13.  Shell脚本的进程状态变量

    1.  $?:获取上一个指令的返回值(0为成功,非零为失败)

       0  成功

       2  权限拒绝

       1~125  运行失败,脚本命令,系统命令错误或参数传递错误

       126  找到该命令了,但是无法执行

       127  未找到要运行的命令

        >128  命令被系统强制结束

[root@mysql-37 scripts]# cat fanhui.sh 
#!/bin/bash
if [ $# -ne 2 ];then
  echo "USAGE: $0 must be two args."
  exit 119
else
  echo yangianbo  
fi

[root@mysql-37 scripts]# /bin/bash /server/scripts/fanhui.sh 
USAGE: /server/scripts/fanhui.sh must be two args.
[root@mysql-37 scripts]# echo $?
119

[root@mysql-37 scripts]# /bin/bash /server/scripts/fanhui.sh 1 2
yangianbo

      $?返回值的用法如下:

        1.  判断命令或脚本或函数等程序是否执行成功。

        2.  若在脚本中调用执行"exit 数字",则会返回这个数字给"$?"变量。

        3.  如果是在函数里,则通过"return 数字" 把这个数字以函数返回值的形式传给"$?"。 

   2.  $$:获取当前执行的shell脚本的进程号

#!/bin/bash
user=`whoami`
echo $$

[zhangshaohua1510@mysql-37 scripts]$ /bin/bash test.sh 
11768

   3.  $_:  获取上一条命令的最后一个参数值

[root@192 tmp]# sh test.sh "I am"  yang jian bo  
I am yang jian bo
I am yang jian bo
I am yang jian bo
I am
yang
jian
bo
[root@192 tmp]# echo $_
bo

   4.  $!:  获取上一次执行脚本的pid 

14.  bash内部变量与内置命令

    1.  echo  在屏幕上输出信息

        语法:  echo args  #后面跟字符串或者变量

        -n:  不换行输出

        -e:  解析转义字符

        常用的转义字符:

          \n:  换行

          \r:  回车

          \t:  制表符

          \b:  退格

          \v:  纵向制表符     

[root@mysql-37 scripts]# echo "yangjianbo";echo "liudehua"   默认自动换行
yangjianbo 
liudehua [root@mysql-37 scripts]# echo -n "yangjianbo ";echo "liudehua" -n不换行
yangjianbo liudehua [root@mysql-37 scripts]# echo -e "yangjianbo\nliudehua" \n换行 yangjianbo liudehua [root@mysql-37 scripts]# echo -e "yangjianbo\tliudehua" \t 制表符tab yangjianbo liudehua [root@mysql-37 scripts]# echo -e "yangjianbo\bliudehua" \b 退格,可以看到少了一个o yangjianbliudehua [root@mysql-37 scripts]# echo -e "yangjianbo\vliudehua" \v 纵向制表符 yangjianbo liudehua

    2.  eval  执行到eval语句,shell读取参数,并将他们组成一个新的命令

#!/bin/bash
echo \$$#      输出的结果为$2

[sysadmin@192 tmp]$ sudo /bin/bash test.sh yangjianbo liudehua
$2

#!/bin/bash
eval "echo \$$#"    eval重新组合了echo $2,所以可以输出liudehua

[sysadmin@192 tmp]$ sudo /bin/bash test.sh yangjianbo liudehua
liudehua

    3.  exec  在不创建新的进程的情况,执行指定的命令,执行完以后,该进程也就结束了。

[sysadmin@192 tmp]$ exec date
2021年 10月 26日 星期二 11:22:10 CST
执行完以后, 直接退出注销了。

    4.  read  标准输入,传给指定变量

[root@yangjianboinbeijing day2]# cat read.sh 
read -p "please input your name:" username
echo $username
[root@yangjianboinbeijing day2]# sh read.sh 
please input your name:yangjianbo
yangjianbo

    5.  history 查看历史记录

    6.  printf  格式化打印

    7.  ulimit  文件系统与程序的限制

    8.  shift  位置参数偏移量,每执行一次,向左移动一个位置

[root@yangjianboinbeijing day2]# set -- "I am" handsome oldboy.
[root@yangjianboinbeijing day2]# echo $#
3
[root@yangjianboinbeijing day2]# echo $1
I am
[root@yangjianboinbeijing day2]# echo $2
handsome
[root@yangjianboinbeijing day2]# echo $3
oldboy.
[root@yangjianboinbeijing day2]# shift
[root@yangjianboinbeijing day2]# echo $1
handsome
[root@yangjianboinbeijing day2]# echo $2
oldboy.
[root@yangjianboinbeijing day2]# echo $3

[root@yangjianboinbeijing day2]# echo $0
-bash

    9.  exit  退出shell

15.  bash变量子串的常用操作

    1.  返回变量的内容

[root@yangjianboinbeijing day2]# OLDBOY="I am oldboy"
[root@yangjianboinbeijing day2]# echo $OLDBOY
I am oldboy

[root@mysql-37 scripts]# echo ${OLDBOY}

I am oldboy

    2.  返回变量值的长度

[root@yangjianboinbeijing day2]# echo ${#OLDBOY}
11

     3.  截取变量值 

[root@yangjianboinbeijing day2]# echo ${OLDBOY:2}    从索引为2的开始截取,包含2
am oldboy
[root@yangjianboinbeijing day2]# echo ${OLDBOY:3}    从索引为3的开始截取,包含3
m oldboy

     3.  截取其中固定的变量值,第一个参数为索引值,第二个为步长

[root@yangjianboinbeijing day2]# echo ${OLDBOY:5:2}   从索引为5的开始截取,截取2个长度
ol

     4.  从开头删除匹配的子字符串  #    ##  

[root@mysql-37 scripts]# OLDBOY="abcABC123ABCabc"
[root@mysql-37 scripts]# echo $OLDBOY
abcABC123ABCabc

[root@mysql-37 scripts]# echo ${OLDBOY#a*c}  从头开始删除最短匹配a*c的字符串
ABC123ABCabc
[root@mysql-37 scripts]# echo ${OLDBOY##a*c}  从头开始删除最长匹配a*c的字符串,所以整个都删掉了  

     5.  从字符串的结尾开始删除  %      %%

[root@mysql-37 scripts]# echo ${OLDBOY}
abcABC123ABCabc
[root@mysql-37 scripts]# echo ${OLDBOY%a*c}  从尾部开始删除最短匹配的a*c
abcABC123ABC
[root@mysql-37 scripts]# echo ${OLDBOY%%a*c}  从尾部开始删除最长匹配的a*c,所以整个都删掉了

     6.  字符串替换

       语法:${变量名/子字符串/替换以后的字符串}  从头开始查找子字符串进行替换,替换第一个

[root@yangjianboinbeijing day2]# echo $OLDBOY
I am oldboy am
[root@yangjianboinbeijing day2]# echo ${OLDBOY/I/is}
is am oldboy am

       语法:${变量名//子字符串/替换以后的字符串}  替换匹配的所有字符串

[root@yangjianboinbeijing day2]# echo ${OLDBOY//am/is}
is is oldboy is

     7.  生产实例

       1.  批量删除文件名称中的固定字符串。   

[root@yangjianboinbeijing day2]# vi rename.sh 
#!/bin/bash
BASEDIR=/server/scripts/day2/testdir
if [ -d $BASEDIR ];then
  cd $BASEDIR
  for file in `ls *.bak`;
    do mv $file ${file//_20190910/}.bak; 替换为空
  done
fi

       2.  修改文件的扩展大写变小写,从结尾开始匹配

for file in `ls *.HTML`;do mv $file ${file//HTML/html};done

       3.  把文件名修改为大写。 

mv $file `echo ${file%.html}|tr "[a-z]" "[A-Z]"`.HTML

       批量改名字案例:http://blog.51cto.com/oldboy/711342  

ls *_20180405.bak|awk -F "_20180405" '{print "mv " $0,$1$2}'|bash

       4.  使用rename命令修改。

rename "_20180405" "" *.bak

rename "HTML" "html" *.HTML

16.  bash变量子串的深入介绍与系统案例分析

    1.  ${value:-word}

       当变量未定义或者值为空,返回值为word的内容,否则返回变量的值。 这个功能用来判断变量是否已定义。如果没有定义,就返回word,定义了就返回定义的值。

[root@yangjianboinbeijing testdir]# result=${test:-word}
[root@yangjianboinbeijing testdir]# echo $test

[root@yangjianboinbeijing testdir]# echo $result
word

     2.  ${value:=word}

          当变量未定义或者值为空,返回word的值的同时将word值赋给value.这个变量的功能可以解决变量没有定义的问题,并确保没有定义的变量始终有值。

[root@yangjianboinbeijing testdir]# result=${test:=word}
[root@yangjianboinbeijing testdir]# echo $test   之前test的值是空的,但是现在已经被赋值word了,这就是与-word的区别
word
[root@yangjianboinbeijing testdir]# echo $result
word

     3.  ${value:?"word"}

       当变量未定义或者值为空,返回word,否则返回定义的值。这个功能用来设定由于变量未定义而报错的具体内容。

[root@yangjianboinbeijing testdir]# result=${test:?word}
-bash: test: word
[root@yangjianboinbeijing testdir]# result=${test:?"not defined"}
-bash: test: not defined

     4.  ${value:+word}

       测试变量是不是存在。如果返回了值,那么说明变量被定义了。否则就是变量没有定义。

[zhangshaohua1510@mysql-37 ~]$ oldboy=${oldgirl:+yangjianbo}
[zhangshaohua1510@mysql-37 ~]$ echo $oldboy  olboy值为空

[zhangshaohua1510@mysql-37 ~]$ echo $oldgirl  oldgirl值为空

[zhangshaohua1510@mysql-37 ~]$ oldgirl=20
[zhangshaohua1510@mysql-37 ~]$ oldboy=${oldgirl:+yangjianbo}  定义了oldgirl,则oldboy返回,后面的yangjianbo
[zhangshaohua1510@mysql-37 ~]$ echo $oldboy                 
yangjianbo
[zhangshaohua1510@mysql-37 ~]$ echo $oldgirl
20

     5.  生产实例

       1.  查看/etc/init.d/httpd文件的内容。

apachectl=/usr/sbin/apachectl
httpd=${HTTPD-/usr/sbin/httpd}   如果HTTPD变量未定义,就使用/usr/sbin/httpd作为值
prog=httpd
pidfile=${PIDFILE-/var/run/httpd/httpd.pid}
lockfile=${LOCKFILE-/var/lock/subsys/httpd}

       2.  生产环境删除文件脚本。

[root@mysql-37 scripts]# cat del20190910.sh 
#!/bin/bash
find ${path-/tmp} -name "*.tar.gz" -type f -mtime +7 |xargs rm -rf {}   #没有定义path,那么就使用/tmp作为路径

17.  变量的数值计算

    常用的算术运算符

      

    常用的算术运算命令

      

    1.  (())   常用,效率最高,只适合整数    

        1.  简单运算

[root@mysql-37 scripts]# ((a=1+1))
[root@mysql-37 scripts]# echo $a
2

  [root@mysql-37 scripts]# echo $((5*9))
  45

        2.  复杂运算

[root@mysql-37 scripts]# echo $((100*(100+1)/2))   
5050

        3.  利用双括号进行比较判断

[root@mysql-37 scripts]# echo $((3>8))
0
[root@mysql-37 scripts]# echo $((3<8))
1

[root@mysql-37 scripts]# if ((8<3))
> then
> echo 1
> else 
> echo 0
> fi
0

        4.  在变量前后使用--和++特殊运算符的表达式          

  

赋值操作

[root@yangjianboinbeijing systemd]# echo $((a+=10))
10


增长减少操作

[root@yangjianboinbeijing systemd]# a=1
[root@yangjianboinbeijing systemd]# echo $((a++))
1
[root@yangjianboinbeijing systemd]# echo $((a++))
2
[root@yangjianboinbeijing systemd]# echo $((a++))
3
[root@yangjianboinbeijing systemd]# echo $((a++))
4

注意:a++与++a的区别,a++先赋值再运算,++a先运算再赋值

 

从1加到100的和是多少?

[root@yangjianboinbeijing systemd]# echo $((100*(100+1)/2))
5050

 

使用命令行的方式,实现一个加减乘除的计算器。

[root@yangjianboinbeijing day2]# vi 02.sh
echo $(($1))

 

[root@yangjianboinbeijing day2]# cat 03.sh 
echo $(($1$2$3))
[root@yangjianboinbeijing day2]# sh 03.sh 1 2
12
[root@yangjianboinbeijing day2]# sh 03.sh 1 +2
3
[root@yangjianboinbeijing day2]# sh 03.sh 1 + 3
4
[root@yangjianboinbeijing day2]# sh 03.sh 1 + 2
3
[root@yangjianboinbeijing day2]# sh 03.sh 1 + 2 + 5
3
[root@yangjianboinbeijing day2]# sh 03.sh 1 +2 +8
11

    2.  let命令  这是shell的内置命令

        实际操作:

[root@yangjianboinbeijing day2]# i=2
[root@yangjianboinbeijing day2]# let i=i+8
[root@yangjianboinbeijing day2]# echo $i
10
注意:如果去掉let,相当于给i赋值:i+8

    3.  expr命令

       用于整数计算  

[root@yangjianboinbeijing day2]# expr 2+2
2+2
[root@yangjianboinbeijing day2]# expr 2 + 2
4
[root@yangjianboinbeijing day2]# expr 2 * 2
expr: 语法错误
[root@yangjianboinbeijing day2]# expr 2 \* 2
4
注意:运算符左右都需要有空格,乘号要使用\进行转义。

[root@yangjianboinbeijing day2]# expr $i + 5
7

[root@yangjianboinbeijing day2]# expr $[2+8]
10

 

expr可以统计字符串的长度

[root@yangjianboinbeijing day2]# expr length "zhongguoren"
11

 

[root@yangjianboinbeijing day2]# expr substr "yangjianbo" 2 2
an

注意:它的截取子串,开始位置为1,而不是变量子串的从0开始。

    4.  bc命令的用法

        bc是unix/linux下的计算器,可以处理小数。       

[root@yangjianboinbeijing ~]# seq -s "+" 100
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
[root@yangjianboinbeijing ~]# seq -s "+" 100|bc
5050

    5.  awk命令的用法

        适合计算整数和小数。       

[sysadmin@192 ~]$ echo "7 3" | awk '{print ($1-$2)}'
4

[sysadmin@192 ~]$ echo "7.8 3.6" | awk '{print ($1-$2)}'
4.2

    6.  使用$[ ]做计算

[root@yangjianboinbeijing ~]# echo $[2*3]
6
[root@yangjianboinbeijing ~]# echo $[2+3]
5

        打印杨辉三角:http://blog.51cto.com/oldboy/756234

    7.  计算变量值的长度的方法比较

         1.  echo ${#变量名}

         2.  echo $(expr length 变量名 )

         3.  echo ${变量名} |wc -m

           结论:使用内部函数耗时小,建议使用第一种方式。

    8.  read用法

         -p:  提示符

         -t:     超时

[root@yangjianboinbeijing day3]# cat read.sh 
#!/bin/bash
read  -t 5 -p "input your number": var1   输入一个值,给变量var1,注意var1前面有空格
echo $var1

     read -p的功能可以用echo -n替换。 

18.  shell的条件测试与比较

    1.  条件测试语法

    

        1.  test <测试表达式> 

[root@yangjianboinbeijing day3]# test -f file && echo true || echo false
false
[root@yangjianboinbeijing day3]# touch file
[root@yangjianboinbeijing day3]# test -f file && echo true || echo false
true

           test !   非的用法   

[root@yangjianboinbeijing day3]# test ! -f file && cat file
cat: file: 没有那个文件或目录
[root@yangjianboinbeijing day3]# touch file
[root@yangjianboinbeijing day3]# test ! -f file && cat file

        2.  [<测试表达式>]

[root@yangjianboinbeijing day3]# [ -f file ] && echo 1 || echo 0
0
[root@yangjianboinbeijing day3]# ll
总用量 4
-rw-r--r-- 1 root root 63 4月   3 20:50 read.sh
[root@yangjianboinbeijing day3]# touch file 
[root@yangjianboinbeijing day3]# [ -f file ] && echo 1 || echo 0
1

[root@yangjianboinbeijing day3]# [ ! -f file ] && echo 1 || echo 0
0
[root@yangjianboinbeijing day3]# ll
总用量 4
-rw-r--r-- 1 root root 0 4月 3 21:07 file
-rw-r--r-- 1 root root 63 4月 3 20:50 read.sh
[root@yangjianboinbeijing day3]# rm -r file
rm:是否删除普通空文件 "file"?y
[root@yangjianboinbeijing day3]# [ ! -f file ] && echo 1 || echo 0
1

         3.  [[<测试表达式>]]

             说明:1和2是等价的,3为扩展的test命令。

             在3中可以使用通配符进行模式匹配。

             &&  ||   > <操作可以应用于[[]]中,但不能应用于[]中。

                对整数进行关系运算,也可以使用shell的算术运算符。  

    2.  文件测试表达式

        1.  关于某个文件名的文件类型判断。

            -f  该文件名是否存在且为文件

            -d  该文件名是否存在且为目录

            -e  该文件名是否存在

            -s  该文件名是否存在且为“非空”文件

        2.  关于某个文件名的权限检测。

            -r  该文件名是否存在且有可读的权限

            -w  该文件名是否存在且有可写的权限

            -x  该文件名是否存在且有可执行的权限

        3.  关于两个文件的之间的比较

            -nt  newer than  判断file1是否比file2新

            -ot  older than  判断file1是否比file2旧

            -ef  判断两个文件是不是指向同一个inode

        4.  例子

            1.  如果测试的是变量,那么变量需要添加引号,否则会有误判。        

[root@192 ~]# echo $oldboy

[root@192 ~]# [ -f $oldboy ] && echo 1 || echo 0
1
[root@192 ~]# [ -f "$oldboy" ] && echo 1 || echo 0
0

            2.  如果测试的是实体路径,那么有没有双引号,结果都是一样的。

[root@192 ~]# [ -f /root/vhost.sh ] && echo 1 || echo 0
1
[root@192 ~]# [ -f "/root/vhost.sh" ] && echo 1 || echo 0
1

    3.  字符串测试表达式

        1.  字符串测试操作符                                                                                            

            注意:字符串测试一定要加双引号,如果不加,会带来逻辑错误。

                  =和!= 两边一定要有空格,如果没有空格,会带来逻辑错误

               =和!=可以判断两个字符串是否相同          

    4.  整数二元比较

                

        1.  建议最好都是用-eq,-ne这些比较符号,因为<>在[ ] 中会出现逻辑错误,如下面的例子

[root@yangjianboinbeijing day2]# [ 2 > 1 ] && echo 1 || echo 0
1
[root@yangjianboinbeijing day2]# [ 2 < 1 ] && echo 1 || echo 0
1
[root@yangjianboinbeijing day2]# [[ 2 < 1 ]] && echo 1 || echo 0
0
如果是单中括号,不要使用符号,使用-eq,-ne,-gt,-lt等;或者使用双中括号。 
双中括号也可以使用-eq -ne -gt

        2.  整数变量的比较

[root@192 ~]# a=55
[root@192 ~]# b=66
[root@192 ~]# [ $a -gt $b ] && echo 1 || echo 0   
0
[root@192 ~]# [ $a -lt $b ] && echo 1 || echo 0 
1

    5.  逻辑操作符

        

 

 

        1.  例子

[root@192 ~]# [ -f /etc/hosts -a -f /etc/resolv.conf ] && echo 1 || echo 0 
1
如果在[ ]中使用&&  ||会有报错。
-bash: [: missing `]'
-bash: -f: command not found
[root@192 ~]# [[ -f /etc/hosts && -f /etc/resolv.conf ]] && echo 1 || echo 0  
1
如果在[[ ]]中使用-a -o也会有报错
-bash: syntax error in conditional expression
-bash: syntax error near `-a'

    6.  测试表达式的区别

        

19.  if条件语句

    1.  单分支结构

        1.  语法:

         if  [条件]

            then

            指令

         fi

         或者

         if  [条件];  then

           指令

         fi

    2.  单分支结构举例

       1.  比较两个数字的大小

[root@yangjianboinbeijing day4]# vi if1.sh 
#!/bin/bash
if [ 3 -gt 2 ];then
    echo 1
fi    
read读入参数
[root@yangjianboinbeijing day4]# cat if2.sh #!/bin/bash read -p "input num1": num1 read -p "input num2": num2 if [ $num1 -gt $num2 ];then echo 1 fi
以位置参数的形式输入值。
[root@yangjianboinbeijing day4]# cat if3.sh #!/bin/bash if [ $1 -gt $2 ];then echo 1 fi
/bin/bash if3.sh 3 1

         2.  开发脚本实现如果/server/scripts/下面存在if4.sh就输出到屏幕。

          注意:如果执行脚本后发现该if4.sh不存在,就自动创建这个if4.sh脚本。  

#!/bin/bash
scriptname=/server/scripts/day4/if4.sh
if [ -e $scriptname ];then
   echo "exist if4.sh"
else
   `touch $scriptname`
fi
变量名称最好用大写,当然小写也没有问题。

       3.  判断系统内存大小,小于100M,就邮件报警。

[root@yangjianboinbeijing day4]# cat if-free.sh 
#!/bin/bash
MEMORY=`free -m | awk 'NR==3{print $4}'`
if [ $MEMORY -lt 2000 ];then
   echo "memory is $MEMORY" |mail -s "memory" 329624434@qq.com
fi 

        4.  判断系统的根目录剩余空间大小,小于50G,就邮件报警

#!/bin/bash
FREE_DISK=`df -h | awk 'NR==2{print $4}'`
JIELUN=`echo "${FREE_DISK//G/} < 50" | bc`
if [ ${JIELUN} -eq 1 ];then
   echo "free_disk is ${FREE_DISK}" |mail -s "free_disk" 329624434@qq.com
fi

    3.  if双分支结构讲解和实例

       1.  双分支结构

          1.  语法:

             if     [条件一];then

               执行语句

                  else

               执行语句

             fi

          2.  实例

             1.  比较两个整数的大小。

[root@yangjianboinbeijing day4]# cat if-double.sh 
#!/bin/bash
a=10
b=11
if [ $a -gt $b ];then
   echo $a
else
   echo $b
fi

             2.  使用传参的方式。 

[root@yangjianboinbeijing day4]# cat if-double2.sh 
#!/bin/bash
if [ $1 -gt $2 ];then
   echo $1
else
   echo $2
fi

             3.  脚本判断需要添加几个参数,如果参数不正确就退出,并报错     

[root@yangjianboinbeijing day4]# cat if-double3.sh 
#!/bin/bash
if [ $# -ne 2 ];then
   echo "ERROR: $0 num1 num2" 
   exit 1
fi
if [ $1 -gt $2 ];then
   echo $1
else
   echo $2
fi

               4.  判断输入的参数是不是数字,使用sed命令。

[root@yangjianboinbeijing day4]# cat if-double5.sh 
#!/bin/bash
if [ $# -ne 2 ];then
   echo "ERROR: $0 num1 num2" 
   exit 1
fi
[ -z "`echo $1|sed 's/[a-z]//g'`" ] && {
   echo "num1 must be int."
   exit 1
}
[ -z "`echo $2|sed 's/[a-z]//g'`" ] && {
   echo "num2 must be int."
   exit 1
}
if [ $1 -gt $2 ];then
   echo $1
else
   echo $2
fi

             5.  使用read读入变量参数,判断为数字,不能为字符串,比较两个数字的大小。

#!/bin/bash
read -p "please input your number: " num1 num2
if [ -z $num1 ];then
   echo "$0 need two args!"
   exit 1
elif [ -z $num2 ];then
   echo "$0 need two args!"
   exit 1
else
   [ -z `echo $num1 | sed 's/[a-z]//g'` ] && {
     echo "num1  must be int"
     exit 1
   }
   [ -z `echo $num2 | sed 's/[a-z]//g'` ] && {
     echo "num2  must be int"
     exit 1
   }
   if [ $num1 -gt $num2 ];then
     echo "$num1 > $num2"
   elif [ $num1 -lt $num2 ];then
     echo "$num1 < $num2"
   else
     echo "$num1=$num2"
   fi
fi 

    4.  if条件语句企业实例

        1.  生产监控mysql服务

            1.  监控mysql服务是否正常启动,如果未正常启动,就启动mysql服务。

通过端口来判断,mysql服务是不是在运行中。
[root@yangjianboinbeijing day4]# cat monitor_mysql.sh #!/bin/bash #monitor mysql server lsof -i:3306 >/dev/null && echo $? >/dev/null if [ $? -eq 0 ];then echo "mysql server is starting!" else echo `systemctl start mysqld.service` >/dev/null echo "mysqld.service start successful!" fi

或者使用netstat -lnt | grep 3306 | wc -l
ps -ef | grep mysqld

                注意:如果过滤进程数,不要让脚本带有mysql字样。涉及到数据库,脚本使用db字样。

                如果没有启动,就启动,再对启动的结果进行判断,如果发现没有成功,就彻底杀死,再启动mysql。

            2.  监控mysql服务,通过连接mysql监控。

[root@yangjianboinbeijing day4]# cat monitor_mysql_pid_port.sh 
#!/bin/bash
#monitor mysql server
mysql -uroot -p123456 -e "select version();" > /dev/null && echo $? >/dev/null
if [ $? -eq 0 ];then
   echo "mysql server is starting!"
else
   echo `systemctl start mysqld.service` >/dev/null
   echo "mysqld.service start successful!"
fi 

            3.  监控mysql服务,通过PHP/JAVA程序监控mysql。

        2.  监控apache和nginx服务

            1.  通过本地端口或进程监控。

#!/bin/bash
NGX_NUM=`netstat -lntp | grep nginx| wc -l`
if [ $NGX_NUM -gt 0 ];then
  echo "nginx is starting"
else
  /etc/init.d/nginx start
  echo "nginx is start sucessful"
fi

            2.  通过远程URL监控。wget或者curl。

#!/bin/bash
if [ `curl -I -s  https://www.baidu.com | awk 'NR==1{print $2 }'` -eq 200 ];then
  echo "nginx is starting"
else 
  echo "nginx is stop"
  /etc/init.d/nginx start
fi      

        3.  使用if语句比较两个整数的大小

            1.  使用传参的方式(要判断传入的参数的个数和传入的是否是整数)

#!/bin/bash
a=$1
b=$2
if [ $# -ne 2 ];then
     echo "USAGE: $0 number is two"
     exit 200
fi

expr $a + 1 > /dev/null
RET_a=$?

expr $b + 1 > /dev/null
RET_b=$?

if [ $RET_a -ne 0 -o $RET_b -ne 0  ];then
      echo "the number must be int"
      exit 100
fi

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

            2.  使用read读取的方式

#!/bin/bash
read -p "insert to two number: " a b

expr $a + 0 > /dev/null
RET_a=$?

expr $b + 0 > /dev/null
RET_b=$?
if [ -z "$a" ] || [ -z "$b" ];then
      echo "the number is two"
      exit 200
fi
if [ $RET_a -ne 0 -o $RET_b -ne 0  ];then
      echo "the number must be int"
      exit 100
fi

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

20.  shell函数

    1.  函数的定义

function 函数名 () {
  指令
  return n  
}

    2.  函数的调用

        1.  不带参数

            函数名

        2.  带参数

            函数名 参数1 参数2

    3.  函数的使用说明

        1.  执行函数时,不需要function和函数后的小括号

        2.  函数的定义必须在执行的程序前面定义或加载

        3.  shell执行系统中各种程序的执行顺序为:系统别名-函数-系统命令-可执行文件

        4.  函数执行时,会调用环境变量,还有自己定义的局部变量以及特殊位置参数

        5.  在shell函数,return的功能与exit类似,return的作用是退出函数,而exit是退出脚本文件

        6.  如果函数在一个单独的文件中,被脚本加载时,需要使用source或者.来加载(一个点)

    4.  范例

        1.  基本

[root@mysql-37 scripts]# cat hanshu.sh 
#!/bin/bash
oldboy
oldboy ()
{
    echo "I am yangjianbo!"
}
function oldgirl ()
{
    echo "I am qiwei!"
}
oldboy
oldgirl

        2.  分离函数体和执行函数

            1.  函数体

[root@mysql-37 scripts]# cat hanshu_fenli.sh 
#!/bin/bash
#created by yangjianbo at 2019-09-17
function yangjianbo (){
    echo "I am liudehua!"
}

               只是定义了函数,但是没有执行函数

            2.  执行函数的脚本

[root@mysql-37 scripts]# cat hanshu_diaoyong.sh 
#!/bin/bash
[ -f /server/scripts/hanshu_fenli.sh ] && source /server/scripts/hanshu_fenli.sh || exit 1
yangjianbo

               注意函数体的脚本,要使用source或者.执行,否则提示找不到函数yangjianbo.  

        3.  带参数[root@mysql-37 scripts]# cat hanshu_fenli.sh 

#!/bin/bash
#created by yangjianbo at 2019-09-17
function yangjianbo (){
    echo "I am liudehua! $1"
}
[root@mysql-37 scripts]# cat hanshu_diaoyong.sh 
#!/bin/bash
[ -f /server/scripts/hanshu_fenli.sh ] && source /server/scripts/hanshu_fenli.sh || exit 23
yangjianbo $1 

        4.  检查网站url

            采用传入参数的方式

#!/bin/bash
. /etc/init.d/functions function usage () { echo "USAGE $0: only one" exit 1 } function check_url () { wget --spider -q -o /dev/null --tries=1 -T 5 $1 if [ $? -eq 0 ];then action "the web server is OK" /bin/true else action "the web server is not OK" /bin/false fi } function main () { if [ $# -ne 1 ];then usage fi check_url $1 } main $*

执行脚本:/bin/bash test.sh www.baidu.com

         5.  简单例子:

[root@yangjianboinbeijing day5]# cat script1.sh 
#!/bin/bash
#Sourse function library.
. /etc/init.d/functions
if [ "$1" == "start" ]
   then
     action "nginx starting." /bin/true
elif [ "$1" == "stop" ]
   then
     action "nginx is stopped." /bin/true
else
     action "nginx is start"  /bin/false
fi 

[root@yangjianboinbeijing day5]# sh script1.sh 
nginx is start                                             [失败]
[root@yangjianboinbeijing day5]# sh script1.sh start
nginx starting.                                            [  确定  ]
[root@yangjianboinbeijing day5]# sh script1.sh stop
nginx is stopped.                                          [  确定  ]

        6.  利用shell函数开发一键优化系统脚本

            1.  针对centos6的一键优化

#1.    更改yum源
#2.    关闭selinux
#3.    关闭iptables
#4.    精简开机自启服务
#5.    提权用户为sudo
#6.    中文字符集
#7.    时间同步
#8.    加大文件描述
#9.    内核优化

                根据以上内容,将每个优化模块写成函数,如下:

#!/bin/bash
if [ $UID -ne 0 ];then
  echo "You must use thr root user!"
fi

function mod_yum(){
  if [ -e /etc/yum.repos.d/CentOS-Base.repo ];then
    mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
    wget -O /etc/yum.repos.d/CentOS-Base.repo http://pub.mirrors.aliyun.com/repo/Centos-7.repo
  fi
}

function close_selinux(){
  sed -i "s#SELINUX=enforcing#SELINUX=disabled#g" /etc/sysconfig/selinux
  setenforce 0 > /dev/null 2>&1
}

function close_iptables(){
  /etc/init.d/iptables stop
  chkconfig iptables off
}

function add_user(){
  if [ `grep 'zhangshaohua1510' /etc/passwd | wc -l` -lt 1 ];then
    useradd zhangshaohua1510 
    echo huazai007@zhenpin.com | passwd --stdin zhangshaohua1510
    \cp /etc/sudoers /etc/sudoers.bak
    echo "zhangshaohua1510 ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
  fi  
}

function time_sync(){
  if [ `rpm -qa | grep ntpdate | wc -l` -lt 1 ];then
     yum install ntpdate -y
  fi
  echo "* 4 * * * /usr/sbin/ntpdate us.pool.ntp.org > /dev/null 2>&1" >> /var/spool/cron/root
}

function openfile(){
  echo -e "* - nproc 65535 \n* - nofile 65535" >> /etc/security/limits.conf
}

main (){
  mod_yum
  close_selinux
  close_iptables
  add_user
  time_sync
  openfile 
}
main

            2.  针对centos7的一键优化

#!/bin/bash
if [ $UID -ne 0 ];then
  echo "You must use thr root user!"
fi

function mod_yum(){
  if [ -e /etc/yum.repos.d/CentOS-Base.repo ];then
    mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
    wget -O /etc/yum.repos.d/CentOS-Base.repo http://pub.mirrors.aliyun.com/repo/Centos-7.repo
  fi
}

function close_selinux(){
  sed -i "s#SELINUX=enforcing#SELINUX=disabled#g" /etc/sysconfig/selinux
  setenforce 0 > /dev/null 2>&1
}

function close_iptables(){
  systemctl stop firewalld
  systemctl disable firewalld
}

function add_user(){
  if [ `grep 'zhangshaohua1510' /etc/passwd | wc -l` -lt 1 ];then
    useradd zhangshaohua1510 
    echo huazai007@zhenpin.com | passwd --stdin zhangshaohua1510
    \cp /etc/sudoers /etc/sudoers.bak
    echo "zhangshaohua1510 ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
  fi  
}

function time_sync(){
  if [ `rpm -qa | grep ntpdate | wc -l` -lt 1 ];then
     yum install ntpdate -y
  fi
  echo "* 4 * * * /usr/sbin/ntpdate us.pool.ntp.org > /dev/null 2>&1" >> /var/spool/cron/root
}

function openfile(){
  echo -e "* - nproc 65535 \n* - nofile 65535" >> /etc/security/limits.conf
}

main (){
  mod_yum
  close_selinux
  close_iptables
  add_user
  time_sync
  openfile 
}
main

        7.  利用shell函数开发rsync服务启动脚本

             1.  服务启动脚本

#!/bin/bash
if [ $# -ne 1 ];then
    echo $"usage:$0 {start|stop|restart}"
    exit 1
fi

if [ "$1" == "start" ];then
   rsync --daemon
   sleep 2
   if [ `netstat -lntp | grep rsync | wc -l` -ge 1 ];then
      echo "rsyncd is started."
      exit 0
   fi
elif [ "$1" == "stop" ];then
   killall rsync &>/dev/null
   sleep 2
   if [ `netstat -lntp | grep rsync |wc -l` -eq 0 ];then
      echo "rsyncd is stopped."
      exit 0
   fi
elif [ "$1" == "restart" ];then
   killall rsync
   sleep 1
   killpro=`netstat -lntp | grep rsync | wc -l`
   rsync --daemon
   sleep 1
   startpro=`netstat -lntp | grep rsync |wc -l`
   if [ $killpro -eq 0 -a $startpro -ge 1 ];then
      echo "rsyncd is restarted."
      exit 0
fi else echo $"usage:$0 {start|stop|restart}" exit 1 fi

            2.  改造为函数

#!/bin/bash
function check_canshu(){
    echo $"usage:$0 {start|stop|restart}"
    exit 1
}

function start(){
   rsync --daemon
   sleep 2
   if [ `netstat -lntp | grep rsync | wc -l` -ge 1 ];then
      echo "rsyncd is started."
      exit 0
   fi
}

function stop(){
   killall rsync &>/dev/null
   sleep 2
   if [ `netstat -lntp | grep rsync |wc -l` -eq 0 ];then
      echo "rsyncd is stopped."
      exit 0
   fi
}

function restart(){
   killall rsync
   sleep 1
   killpro=`netstat -lntp | grep rsync | wc -l`
   rsync --daemon
   sleep 1
   startpro=`netstat -lntp | grep rsync |wc -l`
   if [ $killpro -eq 0 -a $startpro -ge 1 ];then
      echo "rsyncd is restarted."
      exit 0
   fi
}
function main(){
   if [ $# -ne 1 ];then
      check_canshu
   fi      

   if [ "$1" == "start" ];then
      start
   elif [ "$1" == "stop" ];then
      stop
   elif [ "$1" == "restart" ];then
      restart
   else
      check_canshu
   fi
}
main

21.  case条件语句的应用实践

    1.  语法

        case "条件"  in

          值1)   执行1

          ;;

          值2) 执行2

          ;;

          *)    执行3

        esac

    2.  实例

        1.  根据用户的输入判断用户输入的是哪个数字

#!/bin/bash
read -p "input your number:" number
case "$number" in 
  1) 
     echo "you input the number is 1"    
  ;;
  2) 
     echo "you input the number is 2"    
  ;;
  [3-9])
     echo "you input the number is $number"    
  ;;
  *)
     echo $"usage:$0 you input 1-9 number"
esac

        2.  往某个文件添加用户

#!/bin/bash
. /etc/init.d/functions
FILE_PATH=/etc/openvpn_authfile.conf  
[ ! -f $FILE_PATH ] && touch $FILE_PATH
usage () {
  cat << EOF
    USAGE: `basename $0` {-add|-del|-search} username
EOF
}

if [ $UID -ne 0 ];then
   echo "You are not supper user,please call root!"
   exit 1;
fi

if [ $# -ne 2 ];then
   usage
   exit 2
fi

case "$1" in 
    -a|-add)
        shift
        if grep "^$1$" ${FILE_PATH} >/dev/null 2>&1;then
           action $"vpnuser,$1 is exist" /bin/false
           exit
        else
           chattr -i ${FILE_PATH}
           /bin/cp ${FILE_PATH} ${FILE_PATH}.$(date +%F%T)
           echo "$1" >> ${FILE_PATH}
           [ $? -eq 0 ] && action $"Add $1" /bin/true
           chattr +i ${FILE_PATH}
        fi
        ;;
    
    -d|-del)
        shift
        if [ `grep "\b$1\b" ${FILE_PATH}|wc -l` -lt 1 ];then
           action $"vpnuser,$1 is not exist." /bin/false
           exit
        else
           chattr -i ${FILE_PATH}
           /bin/cp ${FILE_PATH} ${FILE_PATH}.$(date +%F%T)
           sed -i "/^${1}$/d" ${FILE_PATH}
           [ $? -eq 0 ] && action $"Del $1" /bin/true
           chattr +i ${FILE_PATH}
        fi
        ;;

     -s|-search)
        shift
        if [ `grep -w "$1" ${FILE_PATH}|wc -l` -lt 1 ];then
           echo $"vpnuser,$1 is not exist.";exit
        else
           echo $"vpnuser,$1 is exist.";exit
        fi
        ;;
      
      *)
        usage
        exit
        
esac

22.  while循环和until循环的应用实践  

    1.  while循环的语法      

while <条件表达式>
do
    指令
done

    2.  while循环执行流程的逻辑图

        

 

    3.  until循环的语法       

until <条件表达式>
do
        指令
done

    4.  当型和直到型循环的基本范例

        1.  持续查看uptime        

#!/bin/bash
while true
do
   uptime
   sleep 2
done

    5.  shell脚本后台运行

        1.  相关用法和说明

          

        2.  例子

            开启两个脚本,都放到后台执行

            /bin/bash  while.sh  &

            /bin/bash  while2.sh  &

            通过jobs命令,查看当前任务的编号

              

 

            想把其中一个放到前台执行,fg 1

            如果想把1又放到后台执行,先ctrl+z暂停,然后执行bg 1,就放到后台执行了。

            想杀掉一个任务,kill  %1        

    6.  while和until的例子

        1.  打印出54321         

#!/bin/bash
i=5
while [ $i -gt 0 ]
do
    echo $i >> /tmp/1.txt
    ((i--))
done

#!/bin/bash
i=5
until [ $i -lt 1 ]
do
    echo $i >> /tmp/2.txt
    ((i--))
done

        2.  计算从1加到100之和

#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
   ((sum=$sum+$i))
   ((i++))
done
echo $sum

    7.  企业实战

        1.  使用while守护进程的方式监控网站,每隔10秒确定一次网站是否正常

#!/bin/bash
. /etc/init.d/functions
if [ $# -ne 1 ];then
    echo $"usage: $0 url"
    exit 1
fi

while true
do
  if [ `curl -o /dev/null -s -w %{http_code} $1 | grep -E "200|301|302" | wc -l` -ne 1 ];then
      action "$1 is error" /bin/false  
    else
      action "$1 is OK" /bin/true
  fi
  sleep 10
done             

        2.  使用shell数组的方式,同时检测多个网站

#!/bin/bash
. /etc/init.d/functions
check_count=0
url_list=(
https://www.baidu.com
https://www.google.com.cn
https://www.zhen.com
)

function wait(){
  echo -n '3秒后,执行检查url操作.';
  for ((i=0;i<3;i++))
  do 
    echo -n ".";sleep 1
  done
  echo
}

function check_url(){
  wait
  for ((i=0;i<`echo ${#url_list[*]}`; i++))
  do
    wget -o /dev/null -T 3 --tries=1 --spider --no-check-certificate ${url_list[$i]} >/dev/null 2>&1
    if [ $? -eq 0 ];then
       action "${url_list[$i]}" /bin/true
    else
       action "${url_list[$i]}" /bin/false
    fi
  done
  ((check_count++))
}

main(){
  while true
  do
    check_url
    echo "--------check count:${check_count}"
    sleep 10
  done
}
main

        3.  分析apache或者nginx访问日志的字节总数

#!/bin/bash
sum=0
exec <$1
while read line
do
  size=`echo $line | awk '{print $10}'`
  expr $size + 1 &> /dev/null
  if [ $? -ne 0 ];then
     continue
  fi
  ((sum=sum+$size))
done
echo "$1:total:${sum}bytes=`echo $((${sum}/1024))`KB"

            其它方法

awk '{print $10}' /usr/local/nginx/logs/test.zhenpin.com.log |awk '{sum+=$1}END{print sum}'

        4.  分析nginx的访问日志,每一个小时一次,并且把访问的web的ip的PV次数超过500的,通过iptables禁止掉。

#!/bin/bash
file=$1

while true
do
  awk '{print $1}' access.log | sort | uniq -c > /tmp/ip.txt
  exec < /tmp/ip.txt
  while read line
  do
    ip=`echo $line | awk '{print $2}'`
    count=`echo $line |awk '{print $1}'`
    if [ $count -gt 500 ]  && [ `iptables -L -n | grep "$ip" | wc -l` -lt 1 ];then
       iptables -I INPUT -s $ip -j DROP
       echo "$line is dropped" >> /tmp/iptables_drop.txt
    fi
  done
  sleep 3600
done 

        5.  分析系统的网络连接数

#!/bin/bash
while true
do
  netstat -ntp | awk -F '[ :]+' '{print $(NF-4)}' | sort | uniq -c > /tmp/ip.txt
  while read line
  do 
    ip=`echo $line | awk '{print $2}'`
    count=`echo $line |awk '{print $1}'`
    if [ $count -gt 500 ]  && [ `iptables -L -n | grep "$ip" | wc -l` -lt 1 ];then
       iptables -I INPUT -s $ip -j DROP
       echo "$line is dropped" >> /tmp/iptables_drop.txt
    fi
  done
  sleep 3600
done 

    8.  while循环按行读文件的方式

        1.  使用exec方式           

exec < a.txt
while read line 
do 
   cmd
done

        2.  cat方式

cat a.txt | while read line 
do 
    cmd
done

        3.  结尾使用<输入重定向

while read line
do
   cmd
done <a.txt  

23.  for和select循环的应用实践

    1.  for循环

        1.  语法:  for 变量 in <变量取值列表>

                do

                  循环体

                done

           第二种语法:

                  for ((exp1;exp2;exp3))

                                                   do

                指令

               done

              例子:

                for ((i=1;i<=3;i++))

                do

                  echo $i

                done

        2.  for循环执行过程

                   

    2.  for循环语句的基础实践

        1.  取值列表为普通数字或字符串

#!/bin/bash
for i in 1 2 3 4 5
do 
  echo $i
done

        2.  取值列表为{}大括号的数字序列

#!/bin/bash
for i in {5..10}
do 
  echo $i
done

        3.  取值列表为某个命令的输出结果

#!/bin/bash
for i in `seq 10 100`
do 
  echo $i
done

 

#!/bin/bash
for i in $(seq 5 10)
do 
  echo $i
done   

        4.  列出某个目录下的所有文件和目录

#!/bin/bash
for i in `ls`
do
    echo $i
done

        5.  批量修改文件名称

#!/bin/bash
for file in `find /data/vendor/653/ -type f`
do
 newfile=`echo $file | sed 's/%//g'`
 mv $file $newfile   
done

 

#!/bin/bash
for i in `ls`
do
    rename ".txt" ".gif" $i
done

        6.  九九乘法表

#!/bin/bash
COLOR='\E[47;30m'
RES='\E[0m'
for num1 in `seq 9`
do
  for num2 in `seq 9`
  do
    if [ $num1 -ge $num2 ];then
       if (((num1*num2)>9));
       then
         echo -ne "${COLOR}$num1*$num2=$((num1*num2))$RES " 后面跟了一个空格
       else
         echo -ne "${COLOR}$num1*$num2=$((num1*num2))$RES  " 后面跟了两个空格
       fi
    fi
  done
echo ""    使用echo是为了换行
done

        7.  计算从1加到100的和

#!/bin/bash
for ((i=1;i<=100;i++))
do
  ((sum=sum+i))
done
echo $sum

        8.  每5秒访问一次百度

#!/bin/bash
for ((i=0;i<5;i++))
do
   curl http://www.baidu.com
   sleep 5
done

    3.  for循环语句的企业高级实战实例

        1.  mysql分库备份脚本

#!/bin/bash
USER=root
PASSWD="123.com"
BACK_PATH=/server/backup
MYSQL_CMD="mysql -u$USER -p$PASSWD"
MYSQL_DUMP="mysqldump -u$USER -p$PASSWD -B"
[ ! -d $BACK_PATH ] && mkdir -p $BACK_PATH
for dbname in `$MYSQL_CMD -e "show databases" |grep -Ev "mysql|performance_schema|information_schema|password|Database|sys"`
do 
   `$MYSQL_DUMP ${dbname} | gzip > $BACK_PATH/${dbname}_$(date +%F).sql.gz`
done   

        2.  mysql分库分表备份脚本

#!/bin/bash
USER=root
PASSWD="123.com"
BACK_PATH=/server/backup
MYSQL_CMD="mysql -u$USER -p$PASSWD"
MYSQL_DUMP="mysqldump -u$USER -p$PASSWD -B"
[ ! -d $BACK_PATH ] && mkdir -p $BACK_PATH
for dbname in `$MYSQL_CMD -e "show databases" |grep -Ev "mysql|performance_schema|information_schema|password|Database|sys"`
do 
    [ ! -d $BACK_PATH/${dbname} ] && mkdir -p $BACK_PATH/${dbname}
    for tbname in `$MYSQL_CMD -e "show tables from ${dbname}" | sed "1d"`
    do
      `$MYSQL_DUMP ${dbname} ${tbname} | gzip > $BACK_PATH/${dbname}/${tbname}_$(date +%F).sql.gz`
    done
done

        3.  检查web服务是否正常,并且发送相关邮件或手机报警信息

 

        4.  批量创建10个账号,密码随机

#!/bin/bash
#created by yangjianbo
rm -rf /tmp/password.log
for num in `seq -w 10`
do
    useradd oldboy_user${num}
    password=`echo $RANDOM | md5sum | cut -c 1-8`
    echo ${password} | passwd --stdin oldboy_user${num}
    echo -e "username:oldboy_user${num}\tpassword:${password}" >> /tmp/password.log
done 

    4.  linux系统产生随机数的6种方法

        1.  通过系统环境变量($RANDOM)

            RANDOM的随机数范围为0~32767,加密性不是很好,可以使用md5sum并截取结果的后n位            

            [root@192 scripts]# echo $RANDOM | md5sum | cut -c 1-8
            af861e2e

        2.  通过openssl产生随机数          

            [root@192 scripts]# openssl rand -base64 60 | md5sum | cut -c 2-9
            72d02d47

        3.  通过date获得随机数          

            [root@192 scripts]# date +%s$N
            1637826212

        4.  通过/dev/urandom配合chksum生成随机数

            [root@192 ~]# head /dev/urandom |cksum

            3483541954 2747

        5.  通过UUID生成随机数            

            [root@192 ~]# cat /proc/sys/kernel/random/uuid 
            cfd1f306-4a1e-4770-9383-963e18851062

        6.  通过expect附带的mkpasswd生成随机数

            yum install expect -y

            mkpasswd相关参数

            

 

        7.  以上所有命令需要结合md5sum使用

            

    5.  select循环语句

        1.  语法

            select   变量名  in  [菜单取值列表]

            do  

              指令

            done

              

        2.  案例

            1.  打印菜单

#!/bin/bash
select name in yangjianbo luoyin yichangkun
do
  echo $name
done

            2.  使用数组做变量列表

#!/bin/bash
array=(liudehua zhangxueyou liming guofucheng)
select name in "${array[@]}"
do
  echo $name
done

            3.  调整select的默认提示符

#!/bin/bash
array=(liudehua zhangxueyou liming guofucheng)
PS3="please select a num from menu:"
select name in "${array[@]}"
do
  echo $name
done            

24.  循环控制及状态返回值的应用实践

    1.  break,continue,exit,return的区别和对比

         

    2.  break,continue,exit功能执行流程图

        1.  break执行流程图

            

 

        2.  continue执行流程图

            

 

        3.  exit的执行流程图

            

    3.  break,continue,exit,return的基础案例        

#!/bin/bash
if [ $# -ne 1 ];then
    echo "usage: break|continue|exit|return"
fi

function test (){
  for ((i=0;i<5;i++))
  do
    if [ $i -eq 3 ];then
       $*;
    fi
    echo $i
  done
  echo "I am in func"
}
test $*
func_test=$?
if [ `echo $* | grep return | wc -l` -eq 1 ];then
    echo "function is exit status: $func_test"
fi
echo "ok"

        1.  传入的参数为break,当for循环条件成立,那么直接跳出了for循环         

[root@192 scripts]# /bin/bash test_break.sh break
0
1
2
I am in func
ok

        2.  传入的参数为continue,当for循环成立,跳出了本次循环,进入了下一次循环

[root@192 scripts]# /bin/bash test_break.sh continue
0
1
2
4
I am in func
ok

        3.  传入的参数为exit,当for循环成立,直接退出了脚本,后面的代码就没有执行

[root@192 scripts]# /bin/bash test_break.sh exit
0
1
2

        4.  传入的参数为return,当for循环成立,退出了函数,后面的代码仍然执行了。

[root@192 scripts]# /bin/bash test_break.sh return
0
1
2
function is exit status: 0
ok

    4.  企业案例

        1.  服务器上添加或删除网卡的ip地址

        2.  把日志中每行的访问字节数所对应的字段数字相加,计算出总的访问量。

#!/bin/bash
sum=0
exec <$1
while read line
do
  size=`echo $line | awk '{print $10}'`
  expr $size + 1 &> /dev/null
  if [ $? -ne 0 ];then
     continue
  fi
  ((sum=sum+$size))
done
echo "$1:total:${sum}bytes=`echo $((${sum}/1024))`KB"

        3.  提供了一个字符串(RANDOM随机数采用md5sum加密后取出连续10位的结果),请破解字符串对应的md5sum前的数字。

for i in {0..32767};do echo `echo $i |md5sum`,$i >> /tmp/1.txt;done

#!/bin/bash
md5char="4fe8bf20ed"
while read line
do
   if [ `echo $line|grep "$md5char"| wc -l` -eq 1 ];then
      echo $line
      break
   fi
done < /tmp/1.txt                      

25.  shell数组的应用实践

    1.  什么是数组

        shell数组就是一个元素集合

    2.  数组的定义与增删改查

        1.  定义

            语法:array=(value1 value2 value3 ...)

        2.  定义变量的方式

            1.  小括号直接赋值

[root@192 scripts]# a=(1 2 3)
[root@192 scripts]# echo ${a[*]}
1 2 3        

            2.  小括号内采用键值对

[root@192 scripts]# a=([15]=1 [13]=2 [14]=3)
[root@192 scripts]# echo ${a[*]} 
2 3 1

            3.  分别定义数组变量

[root@192 scripts]# a[0]=yangjianbo;a[1]=luoying;a[2]=yichangkun
[root@192 scripts]# echo ${a[*]}
yangjianbo luoying yichangkun

            4.  动态定义数组变量

[root@192 scripts]# echo ${a[*]}
1.gif 2.gif 3.gif case.sh check_ip.sh check_netstat.sh check_url.sh index.html ls_test.sh memeory_free.sh openvpn_user.sh rsynd rsynd-funcation solrslow.sh test_break.sh test_centos7.sh test_random.sh test_select.sh test.sh test_url.sh until.sh while2.sh while.sh

        3.  打印数组元素

            echo ${a[0]}  默认下标从0开始的

            echo ${a[*]}  打印出所有的元素

        4.  打印数组元素的个数

            echo ${#a[*]}

            echo ${#a[@]}

        5.  数组赋值

[root@192 scripts]# a[0]=liudehua
[root@192 scripts]# echo ${a[0]} 
liudehua

        6.  数组删除

[root@192 scripts]# unset a[0]     删除某个元素
[root@192 scripts]# unset a    删除整个变量

        7.  数组内容的截取和替换

            1.  内容截取

[root@192 ~]# echo ${a[*]}
a b c d e f g h i j k l m n o p q r s t u v w x y z

[root@192 ~]# echo ${a[*]:1:4}    第一个冒号后面的数字表示从下标为1开始,第二个冒号后面的数字表示截取的长度
b c d e

            2.  内容替换

[root@192 ~]# echo ${a[*]/one/1}
1 two three four five six seven eight nine ten         

    3.  shell数组开发实践

        1.  通过C语言型的for循环语句打印数组元素

#!/bin/bash
a=(one two three four five)
for ((i=0;i<${#a[*]};i++))
do
  echo ${a[i]}
done

            使用普通的for循环

#!/bin/bash
a=(one two three four five)
for i in ${a[*]}
do
   echo $i
done

        2.  通过while循环语句打印数组元素

#!/bin/bash
a=(one two three four five)
i=0
while ((i<${#a[*]}));
do
  echo ${a[i]}
  ((i++))
done

        3.  将命令结果作为数组元素定义并打印

#!/bin/bash
a=($(ls /root))
for i in ${a[*]};
do
  echo $i
done

    4.  高级实战案例

        1.  利用bash for循环打印下面这句话中字母数不大于6的单词

            I am oldboy teacher welcome to oldboy training class

            1.  通过数组实现

#!/bin/bash
a=(I am oldboy teacher welcome to oldboy training class)
for ((i=0;i<${#a[*]};i++))
do
   if [ ${#a[i]} -le 6 ];then
      echo ${a[i]}
   fi
done            

            2.  通过for循环实现

#!/bin/bash
a="I am oldboy teacher welcome to oldboy training class"
for i in $a;
do
  if [ ${#i} -le 6 ];then
     echo $i
  fi
done

            3.  通过awk实现

[root@192 scripts]# a="I am oldboy teacher welcome to oldboy training class"
[root@192 scripts]# echo $a | awk '{for (i=1;i<=NF;i++) if (length($i)<=6) print $i}'

        2.  批量检查多个网站地址是否正常

#!/bin/bash
. /etc/init.d/functions
check_count=0
url_list=(
https://www.baidu.com
https://www.google.com.cn
https://www.zhen.com
)

function wait(){
  echo -n '3秒后,执行检查url操作.';
  for ((i=0;i<3;i++))
  do 
    echo -n ".";sleep 1
  done
  echo
}

function check_url(){
  wait
  for ((i=0;i<`echo ${#url_list[*]}`; i++))
  do
    wget -o /dev/null -T 3 --tries=1 --spider --no-check-certificate ${url_list[$i]} >/dev/null 2>&1
    if [ $? -eq 0 ];then
       action "${url_list[$i]}" /bin/true
    else
       action "${url_list[$i]}" /bin/false
    fi
  done
  ((check_count++))
}

main(){
  while true
  do
    check_url
    echo "--------check count:${check_count}"
    sleep 10
  done
}
main

        3.  开一个守护进程脚本,每30秒监控一次mysql主从复制是否正常,如果有异常,就发出邮件报警    

#!/bin/sh
USER=root
PASSWORD=123456
PORT=3307
error=(1158 1159 1008 1007 1062)
MYSQLCMD="mysql -u$USER -p$PASSWORD -S /data/$PORT/mysql.sock"

is_run(){
  [ `lsof -i:$PORT|wc -l` -lt 2 ]&&{
    echo "mysql server is stopped"
    exit 1
  }
}

status_array(){
 status=($($MYSQLCMD -e "show slave status\G"|egrep "_Running|Last_Errno|Behind_Master"|awk '{print $NF}'))
}

status_error(){
for((i=0;i<${#error[*]};i++))
do
  if [ "$1" == "${error[$i]}" ]
    then
      $MYSQLCMD -e "stop slave;set global sql_slave_skip_counter=1;start slave;"
  else 
      echo "MySQL slave is failed, errorno is $1"
  fi
done
}

judge_slave(){
  status_array
  if [ "${status[0]}" == "Yes" -a "${status[1]}" == "Yes" -a "${status[3]}" = "0" ]
  then
    echo "MySQL slave is ok"
  else
    status_error ${status[2]}
  fi
}

main(){
while true
do
  is_run
  judge_slave
  sleep 60
done
}
main     

26.  shell脚本开发规范

    1.  脚本基本规范

        1.  第一行指定脚本解释器。/bin/bash

        2.  从第二行开始添加日期,作者,功能介绍,版本号

        3.  脚本命名以sh结尾

        4.  成对出现的符号,一次性写完

        5.  流程控制语句一次性写完  

        6.  代码缩进让代码易读

        7.  字符串赋值给变量要加双引号    

    2.  shell变量命名及引用变量规范

        1.  全局变量

            必须大写

        2.  局部变量

            驼峰语法

            首字母大写

        3.  变量的引用规范

            使用大括号引用变量  ${变量名}

            变量内容为字符串时,加双引号  "${变量名}"

            变量内容为整数时,直接使用 $变量名 来引用            

    3.  shell函数的命名及函数定义规范

        1.  函数名首字母大写

    4.  shell脚本高级命名规范

        1.  常规shell使用.sh后缀

        2.  模块的启动或停止  start_模块名.sh  stop_模块名.sh

        3.  监控脚本*_mon.sh

        4.  控制脚本*_ctl.sh

27.  shell脚本的调试

    1.  常见脚本错误范例

        1.  if条件语句缺少结尾关键字

        2.  循环语句缺少关键字

        3.  成对符号落了单

        4.  中括号两端没空格  

    2.  shell脚本调试技巧

        1.  使用dos2unix命令处理在windows下开发的脚本

            dos2unix  while.sh

            yum install dos2unix -y

        2.  使用echo命令调试

        3.  使用bash命令参数调试

            -n  不会执行脚本,只会检查语法

            -v  先将脚本的内容输出到屏幕上,然后执行脚本,如果有错误,也会给出错误提示

            -x  将执行的脚本内容及输出显示到屏幕上,常用的参数            

28.  shell脚本开发环境的配置和优化

    1.  配置文件.vimrc的重要参数

set nocompatible
set history=100
filetype on
filetype plugin on
filetype indent on
set autoread
set mouse=a
syntax enable
set cursorline
hi cursorline guibg=#00ff00
hi CursorColumn guibg=#00ff00
set nofen
set fdl=0
set expandtab
set tabstop=4
set shiftwidth=4
set softtabstop=4
set smarttab
set ai
set si
set wrap
set sw=4
set wildmenu
set ruler
set cmdheight=1
set lz
set backspace=eol,start,indent
set whichwrap+=<,>,h,l
set magic
set noerrorbells
set novisualbell
set showmatch
set mat=2
set hlsearch
set ignorecase
set encoding=utf-8
set fileencodings=utf-8
set termencoding=utf-8
set smartindent
set cin
set showmatch
set guioptions-=T
set guioptions-=m
set vb t_vb=
set laststatus=2
set pastetoggle=<F9>
set background=dark
highlight Search ctermbg=black  ctermfg=white guifg=white guibg=black
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
    if expand("%:e") == 'sh'
        call setline(1, "#!/bin/bash")
        call setline(2, "#Author:Ray")
        call setline(3, "#Blog:https://blog.51cto.com/14154700")
        call setline(4, "#Time:".strftime("%F %T"))
        call setline(5, "#Name:".expand("%"))
        call setline(6, "#Version:V1.0")
        call setline(7, "#Description:This is a test script.")
    endif
endfunc

        对于上面参数的解释

"关闭兼容模式
set nocompatible
"设置历史记录步数
set history=100
"开启相关插件
filetype on
filetype plugin on
filetype indent on
"当文件在外部被修改时,自动更新该文件
set autoread
"激活鼠标的使用
set mouse=a
"""""""""""""""""""""
" => 字体和颜色
"""""""""""""""""""""
"开启语法
syntax enable
"设置字体
"set guifont=dejaVu\ Sans\ MONO\ 10
"
""设置配色
"colorscheme desert
"高亮显示当前行
set cursorline
hi cursorline guibg=#00ff00
hi CursorColumn guibg=#00ff00
"""""""""""""""""""""
" => 代码折叠功能 by oldboy
"""""""""""""""""""""
"激活折叠功能
set foldenable
"设置按照语法方式折叠(可简写set fdm=XX)
"有6种折叠方法:
"manual 手工定义折叠
"indent 更多的缩进表示更高级别的折叠
"expr   用表达式来定义折叠
"syntax 用语法高亮来定义折叠
"diff   对没有更改的文本进行折叠
"marker 对文中的标志进行折叠
set foldmethod=manual
"设置折叠区域的宽度
"如果不为0,则在屏幕左侧显示一个折叠标识列
"分别用“-”和“+”来表示打开和关闭的折叠。
set foldcolumn=0
"设置折叠层数为3
setlocal foldlevel=3
"设置为自动关闭折叠
set foldclose=all
"用空格键来代替zo和zc快捷键实现开关折叠
"zo  O-pen a fold   (打开折叠)
"zc  C-lose a fold  (关闭折叠)
"zf  F-old creation (创建折叠)
nnoremap <space> @=((foldclosed(line('.')) < 0)  'zc' : 'zo')<CR>
"""""""""""""""""""""
" => 文字处理 by oldboy
"""""""""""""""""""""
"使用空格来替换Tab
set expandtab
"设置所有的Tab和缩进为4个空格
set tabstop=4
"设定 << 和 >> 命令移动时的宽度为4
set shiftwidth=4
"使得按退格键时可以一次删掉4个空格
set softtabstop=4
set smarttab
"缩进,自动缩进(继承前一行的缩进)
"set autoindent命令关闭自动缩进,是下面配置的缩写。
"可使用autoindent命令的简写,即 “:set ai” 和 “:set noai”。
"还可以使用“ :set ai sw=4”在一个命令中打开缩进并设置缩进级别。
set ai
"智能缩进
set si
"自动换行
set wrap
"设置软宽度
set sw=4
"""""""""""""""""""""
" => Vim 界面 by oldboy
"""""""""""""""""""""
"Turn on WiLd menu
set wildmenu
"显示标尺
set ruler
"设置命令行的高度
set cmdheight=1
"显示行数
"set nu
"Do not redraw, when running macros.. lazyredraw
set lz
"设置退格
set backspace=eol,start,indent
"Bbackspace and cursor keys wrap to
set whichwrap+=<,>,h,l
"Set magic on(设置魔术)
set magic
"关闭遇到错误时的声音提示
"关闭错误信息响铃
set noerrorbells
"关闭使用可视响铃代替呼叫
set novisualbell
"显示匹配的括号([{和}])
set showmatch
"How many tenths of a second to blink
set mat=2
"搜索时高亮显示搜索到的内容
set hlsearch
"搜索时不区分大小写
"还可以使用简写(“:set ic” 和 “:set noic”)
set ignorecase
"""""""""""""""""""""
" => 编码设置
"""""""""""""""""""""
"设置编码
set encoding=utf-8
"设置文件编码
set fileencodings=utf-8
"设置终端编码
set termencoding=utf-8
"""""""""""""""""""""
" => 其他设置 by oldboy 2010
"""""""""""""""""""""
"开启新行时使用智能自动缩进
set smartindent
set cin
set showmatch
"隐藏工具栏
set guioptions-=T
"隐藏菜单栏
set guioptions-=m
"置空错误铃声的终端代码
set vb t_vb=
"显示状态栏 (默认值为 1, 表示无法显示状态栏)
set laststatus=2
"粘贴不换行问题的解决方法
set pastetoggle=<F9>
"设置背景色
set background=dark
"设置高亮相关
highlight Search ctermbg=black  ctermfg=white guifg=white guibg=black

        vim路径等配置

        

29.  Expect自动化交互式程序应用实践

    1.  安装Expect软件

        yum install expect -y

    2.  Expect程序自动交互的重要命令及实践

        1.  spawn命令

            语法:  spawn   [选项]   [需要自动交互的命令或程序]

            spawn ssh root@192.168.10.200 uptime

        2.  expect命令

            获取spawn命令执行后的信息,看是否匹配,匹配就执行expect后面的动作

            语法:  expect   表达式    [动作]

            expect   "*password"    {send     "123456\r"}

            expect eof

            或者expect与send放在不同行

            expect   "*password"

            send     "123456\r"

            expect eof

            多次匹配不同的字符串

            expect {

              "yes/no"        {exp_send   "yes\r";exp_continue}

              "*password"   {exp_send  "123456\r"}

            }

            expect  eof

        3.  send命令

            \r表示回车

            \n表示换行

            \t表示制表符

        4.  exp_continue命令

            表示继续匹配

        5.  常用命令总结

            

    3.  expect程序变量

        1.  普通变量

            语法:  set    变量名   变量值

            set    password   "123456"

            打印变量

            puts    $变量名

        2.  特殊参数变量

            1.  位置参数

            语法:[lindex $argv n]

            例子:

#!/usr/bin/expect
#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir  [lindex $argv 2]
send_user "$file\t$host\t$dir\n"
puts "$file\t$host\t$dir\n"                      

            结果:

[root@192 scripts]# expect teshu.exp yangjianbo.log 192.168.1.130 /tmp
yangjianbo.log  192.168.1.130   /tmp
yangjianbo.log  192.168.1.130   /tmp

            2.  传参的个数和脚本名参数

#!/usr/bin/expect
#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir  [lindex $argv 2]
send_user "$file\t$host\t$dir\n"
puts "$file\t$host\t$dir\n"
puts "$argc\n"     传参个数$argc
puts "$argv0\n"     传参脚本名 $argv0,没有空格的

    4.  expect的if条件语句

        1.  语法

if   {条件表达式}   {
   指令
}

if    {条件表达式}   {
   指令
}  else  {       #固定格式,不能修改
   指令
}

        2.  例子

            1.  判断传参个数

#!/usr/bin/expect
if { $argc !=3 } {
    send_user  "usage: expect $argv0 file host dir\n"
    exit
}

#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir  [lindex $argv 2]
send_user "$file\t$host\t$dir\n"
puts "$file\t$host\t$dir\n"
puts "$argc\n"
puts "$argv0\n"

            2.  判断传参个数,不管是否符合都给予提示

#!/usr/bin/expect
if { $argc !=3 } {
    send_user  "usage: expect $argv0 file host dir\n"
    exit
} else {
  puts "good."
}

#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir  [lindex $argv 2]
send_user "$file\t$host\t$dir\n"
puts "$file\t$host\t$dir\n"
puts "$argc\n"
puts "$argv0\n"

    5.  expect中的关键字

        1.  eof

            用于匹配结束符

        2.  timeout

            控制时间的关键字变量

    6.  生产场景下的实例

        1.  批量执行命令

#!/usr/bin/expect
if { $argc !=2 }  {
    send_user "usage:expect $argv0 ip cmd \n"
    exit
}

#define var
set ip [lindex $argv 0]
set cmd [lindex $argv 1]
set password "**********"

spawn ssh -p11984 zhangshaohua1510@$ip $cmd
expect {
    "yes/no"    {send "yes\r";exp_continue}
    "*password" {send "$password\r"}

}
expect eof

          再加一个shell脚本,for循环,用来遍历多个机器ip

[root@node1 scripts]# cat for.sh 
#!/bin/bash
#Author:yangjianbo
#Blog:https://www.cnblogs.com/yangjianbo
#Time:2021-12-08 09:36:39
#Name:for.sh
#Version:V1.0
#Description:This is a test script.
if [ $# -ne 1 ];then
    echo $"USAGE: $0 cmd"
    exit 1
fi
cmd=$1
for i in 192 193 194
do 
    expect /server/scripts/ceshi.exp 192.168.2.$i "$cmd"
done

        2.  批量复制文件

#!/usr/bin/expect
if { $argc != 3 } {
    puts "usage: expect $argv0 file host dir"
    exit
}

#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir  [lindex $argv 2]
set password "***************"
spawn scp -P11984 -rp $file zhangshaohua1510@$host:$dir
expect {
    "yes/no"    {send "yes\r";exp_continue}
    "*password" {send "$password\r"}
}
expect eof

          再加一个脚本,执行for循环,遍历多台机器

#!/bin/bash
#Author:yangjianbo
#Blog:https://www.cnblogs.com/yangjianbo
#Time:2021-12-08 09:36:39
#Name:for.sh
#Version:V1.0
#Description:This is a test script.
if [ $# -ne 2 ];then
    echo $"USAGE: $0 file dir"
    exit 1
fi
file=$1
dir=$2
for i in 192 193 194
do 
    expect /server/scripts/copy.exp $file 192.168.2.$i $dir
done  

        3.  自动化部署ssh密钥认证+ansible的项目实战

#!/usr/bin/expect
if { $argc != 2 } {
        send_user "usage: expect $argv0 file host\n"
                exit
}

#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set password "huazai007@zhenpin.com"
spawn ssh-copy-id -i $file "-p 11984 zhangshaohua1510@$host"
expect {
        "yes/no"    {send "yes\r";exp_continue}
        "*password" {send "$password\r"}
}
expect eof 

            加一个for循环脚本,遍历多个机器

#!/bin/bash
for i in 191 192 193 194
do
    expect /server/scripts/ssh-copy.exp ~/.ssh/id_rsa.pub 192.168.2.$i
done

  

  

        

    

  

          

  

      

 

  

          

                          

posted @ 2018-03-30 23:57  奋斗史  阅读(668)  评论(0)    收藏  举报