Shell脚本

ShellScript概念

程序

程序:算法+数据结构,数据是程序的核心javascript

  • 算法:处理数据的方式php

  • 数据结构:数据在计算机中的类型和组织方式html


分类

按程序编程风格:java

  • 过程式:以指令为中心,数据服务于指令node

  • 对象式:以数据为中心,指令服务于数据python


按程序运行方式:mysql

  • 编译运行:高级语言-->编译器-->机器代码-->执行linux

    源代码须要编译器转换为程序文件,运行程序文件时不须要编译器的参与,所以程序执行效率高ios

    好比:C,C++nginx

  • 解释运行:高级语言-->执行-->解释器-->机器代码

    源代码不须要事先编译,运行时启动解释器然后由解释器边解释边运行,所以效率比较低

    好比:shell,python,php,JavaScript,perl


按编程实现是调用库仍是调用外部的程序文件:

  • 非完整编程语言:利用系统上的命令及编程组件进行编程,shell脚本

  • 完整的编程语言:利用库和编程组件进行编程,非shell脚本


按编程模型:

  • 面向过程编程语言

    以指令为中心来组织代码,以过程或函数为基础,数据服务于代码,围绕指令来组织数据;这种语言对底层硬件,内存等操做比较方便,可是写代码和调试维护等会很麻烦。 他们按照顺序执行,选择执行,循环执行 好比:C bash C++ python

  • 面向对象的编程语言

    以数据为中心来组织代码,以对象做为基本程序结构单位的程序设计语言,指令服务于数据,围绕数据来组织指令;指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。语言中提供了类、继承等成分。 对象:特定的数据类型 类class:实例化成为对象 好比:Java C++ python


综上所述可知:

shell脚本编程属于解释运行过程式编程语言且依赖于外部程序文件来运行


编程基本结构

  • 各类系统命令的组合

  • 数据存储:变量、数组

  • 表达式: a+b

  • 语句: if

bash脚本

一种为shell编写的脚本程序;

是Linux命令的堆砌;

但因为Linux中不少命令不具备幂等性,须要设定程序逻辑来判断运行条件是否知足,以免其运行中发生错误

幂等性

即一个操做,不论执行多少次,产生的效果和返回的结果都是同样的!


ShellScript做用

减小重复性的工做

  • 自动化经常使用命令

  • 执行系统管理和故障排除

  • 建立简单的应用程序

  • 处理文本或文件

  1. 自动化安装操做系统

    1. kickstart 底层shell脚本

    2. cobbler 底层shell脚本

  2. 初始化操做系统 SSH优化 关闭SElinux 防火墙放行须要的端口(80 443 22修改 10050) YUM源 时间同步 系统最大描述符 内核参数优化 字符集优化 禁止开机自动启动 修改主机名称 (修改公司网卡名称)... 手动操做要注意 命令行安全 bash的命令历史 写入shell脚本(经常使用)

  3. 安装服务 Nginx PHP MySQL Rsync等等... 针对不一样的版本写入shell脚本自动安装

  4. 配置服务

  5. 启动服务 全部的服务底层的启动方式都是使用的shell脚本 公司本身研发的程序 nohup python3.5 test.py --redis --port --mysql --port -a xxxx & 复制一下 写入脚本 sh start_test_py.sh 如何中止py程序 ps axu|grep test.py |grep -v grep|awk '{print $2}'|xargs kill -9 复制一下 写入脚本 sh stop_test_py.sh 把py的进程的端口和PID取出来 来判断是否运行

  6. 日志统计 查看程序运行的状况 统计咱们须要的数据 日志切割 定时任务+脚本 统计数据 定时任务+脚本 ---> 经过邮件发送给管理员 ELK 日志统计界面 py开发日志界面 py界面----> 数据库 <----数据 日志展现

  7. 监控 监控服务 服务端口是否存在 服务是否存在 服务器的硬件资源使用状况 状态 日志 网络 Zabbix 经过脚本统计---> 测试---> 添加到zabbix服务 (cacti监控流量 Nagios宽带运营商 IT公司)

ShellScript规范

脚本文件建立约定

  1. 脚本存放在固定的目录/server/scripts统一管理

  2. 脚本使用.sh结尾,让咱们能识别是shell脚本

  3. 脚本命名,见名知其意

  4. 脚本内的注释最好不用中文(能够用)

  5. 脚本内的成对的符号一次性写完再写内容

脚本代码开头约定

  1. 默认解析器: #!/usr/bin/env bash 会本身判断使用的shell是什么,并加载相应的环境变量

  2. 程序名,避免更改文件名为没法找到正确的文件

  3. 版本号

  4. 修改时间

  5. 做者相关信息

  6. 该程序的做用,及注意事项

  7. 最后是各版本的更新简要说明

 #!/usr/bin/env bash
 # ------------------------------------------
 # Filename: hello.sh
 # Revision: 1.0
 # Date: 2020/10/22
 # Author: liupenghui
 # Email: 15094034633@163.com
 # Description: This is the first script
 # Copyright: 2020 liu
 # License: GPL
 # ------------------------------------------
 echo “hello world”

注释

 # 主函数 []<-()                   <-------函数注释这样写
 function main(){
  local var="Hello World!!!"
   echo ${var}
 }
 # info级别的日志 []<-(msg:String) <-------带入参的函数注释
 log_info(){
   echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: [info] $*" >&2
 }
 # error级别的日志 []<-(msg:String) <-------带入参的函数注释
 log_error(){
   # todo [error]用红色显示         <------函数内注释
  local msg=$1 # 将要输出的日志内容 <------变量的注释紧跟在变量的后面
   if [[ x"${msg}" != x"" ]];then
     # 注释                       <-------函数内注释 `#` 与缩进格式对整齐
     echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]:[error] $*" >&2
   fi
 }

缩进

  1. 使用两个空格进行缩进,不使用tab缩进

  2. 不在一行的时候使用 \ 进行换行,使用 \ 换行的原则是整齐美观

 #!/usr/bin/env bash
 # 脚本使用帮助文档 []<-()
 manual(){
   cat "$0"|grep -v "less \"\$0\"" \
          |grep -B1 "function "   \
          |grep -v "\\--"         \
          |sed "s/function //g"   \
          |sed "s/(){//g"         \
          |sed "s/#//g"           \
          |sed 'N;s/\n/ /'       \
          |column -t             \
          |awk '{print $1,$3,$2}' \
          |column -t
 }
 function search_user_info(){
  local result=$(httpclient_get --cookie "${cookie}" \
                                          "${url}/userName=${user_name}")
 }

执行方式

  1. 使用解释器(sh或者bash)运行脚本,开启一个子shell运行脚本内容

  2. 执行脚本绝对路径或相对路径,须要脚本有执行权限

  3. 使用. 或者source运行脚本,在当前父shell中运行里面的内容

  4. 传递给|bash执行,不经常使用

能够给脚本加上执行权限chmod +x /server/scripts/one.sh,并将脚本的绝对路径添加到path变量中echo PATH=/server/scripts:$PATH > /etc/profile.d/shell.sh,使变量当即生效. /etc/profile.d/shell.sh,就能够像运行普通命令同样直接执行脚本了!

父进程和子进程

 [root@oldboyedu-lnb ~]# name=f;(echo $name;name=z;echo $name);echo $name # 小括号会开启子进程,赋予的变量,只在小括号内有效,执行完命令后,就会退出子进程。
 f
 z
 f
 [root@oldboyedu-lnb ~]# name=f;{ echo $name;name=z;echo $name; };echo $name # 大括号不会开启子进程,在当前进程有效,执行完命令后,留在当前进程。
 f
 z
 z

调试

 # 语法检测
 bash -n /path/to/script
 # 调试执行
 bash -x /path/to/script

echo打印颜色字

 echo -e "\033[31malong\033[0m"       显示红色along
 echo -e "\033[1;31malong\033[0m"     高亮显示红色along
 echo -e "\033[41malong\033[0m"       显示背景色为红色的along
 echo -e "\033[31;5malong\033[0m"     显示闪烁的红色along
 
 color=$[$[RANDOM%7]+31]
 echo -ne "\033[1;${color};5m*\033[0m" 显示闪烁的随机色along

ShellScript变量

命名法则

  1. 不能使程序中的保留字:例如if,for

  2. 只能使用数字、字母及下划线,且不能以数字开头

  3. 见名知义

  4. 统一命名规则:驼峰命名法

  • 建议:

  1. 全局变量大写

  2. 局部变量小写

  3. 函数名小写

格式

  1. 变量赋值使用 = 等号,左右不能留有空格

  2. 使用变量时推荐使用 "${}" 双引号和大括号包裹

 var1="Hello World"   # 正确,推荐使用双引号
 var2=6.70            # 小数
 var3="${var1}"       # 推荐 双引号和大括号 包裹

单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的,单引号字串中不能出现单引号(对单引号使用转义符后也不行)。 双引号中的普通字符都会原样输出,可使用$引用变量,双引号中能够出现单引号。

  1. 常量必定要定义成readonly

  2. 函数中的变量要用local修饰,定义成局部变量,这样在外部遇到重名的变量也不会影响

 web="www.chen-shang.github.io"
 function main(){
  local name="chenshang" # 这里使用local定义一个局部变量
  local web="${web}"     # 这里${}内的web是全局变量,以后在函数中在使用web变量都是使用的局部变量
  local web2="${web}"    # 对于全局变量,虽然在使用的时候直接使用便可,但仍是推荐使用一个局部变量进行接收,而后使用局部变量,以防止在多线程操做的时候出现异常(至关于java中的静态变量在多线程中的时候须要注意线程安全同样,但常量除外)
 }
  1. 变量一经定义,不容许删除(也就是禁用unset命令)

类型

强类型:

变量不通过强制转换,它永远是这个数据类型,不容许隐式的类型转换。通常定义变量时必须指定类型、参与运算必须符合类型要求;调用未声明变量会产生错误 如:java , c# ,python

弱类型:

语言的运行时会隐式作数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用 如:bash 不支持浮点数,php,javascript


shell中变量的基本类型就是String、数值(能够本身看作Int、Double之类的)、Boolean。

Boolean 实际上是Int类型的变种, 在shell中0表明真、非0表明假,因此每每在shell脚本中用 readonly TURN=0 && readonly FALSE=1

根据变量的生效范围等标准划分下面变量类型:

局部变量:生效范围为当前shell进程;对当前shell以外的其它shell进程,包括当前shell的子shell进程均无效

环境变量:生效范围为当前shell进程及其子进程

本地变量:生效范围为当前shell进程中某代码片段,一般指函数

位置变量:$1, $2, ...来表示,用于让脚本在脚本代码中调用经过命令行传递给它的参数

特殊变量:$?, $0, $*, $@, $#,$$

局部变量

  • 变量赋值:name=‘value’

  • 变量引用:${name} 或者 $name

(1) 能够是直接字串:name=“root"
(2) 变量引用:name="$USER"
(3) 命令引用:name=`COMMAND`
            name=$(COMMAND)
  • " " 弱引用,其中的变量引用会被替换为变量值

  • ' ' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串

  • 显示已定义的全部变量:set

  • 删除变量:unset name

环境变量

  • 变量声明、赋值: export name=VALUE declare -x name=VALUE

  • 变量引用: $name, ${name}

  • 显示全部环境变量: env print env export declare -x

  • 删除变量:unset name

  • bash内建的环境变量 PATH SHELL USER UID HOME PWD SHLVL LANG MAIL HOSTNAME HISTSIZE _ 下划线

只读变量

只能声明,但不能修改和删除

  • 声明只读变量: readonly name declare -r name

  • 查看只读变量: readonly -p

位置变量

在脚本代码中调用经过命令行传递给脚本的参数

$1, $2, ... 对应第一、第2等参数,shift [n]换位置,从$9之后须要加{}表示总体
set --     清空全部位置变量

特殊变量

  $0         脚本文件名称,若是全路径执行则带全路径,可使用basename只获取名字
  $#         传递给脚本的参数的个数
  $*         传递给脚本的全部参数
  $@         传递给脚本的全部参数
  "$*"       所有参数合为一个字符串,可在循环中验证
  "$@"       每一个参数为独立字符串,可在循环中验证
  $$         运行脚本的PID
  $!         上一个运行脚本的PID
  $_         当前命令行的最后一个参数, 相似于ESC .

$$$BASHPID区别:二者都是当前进程的编号,可是$BASHPID更精确

basename

只输出路径的基名

[root@oldboyedu-lnb ~]#  basename /etc/passwd
passwd

进程使用退出状态来报告成功或失败

0       表明成功
1-255  表明失败
$?      保存上一条命令的退出状态

例如:

ping -c1 -W1 hostdown &> /dev/null
echo $?

exit [n]     自定义退出状态码

注意:

  • 脚本中一旦遇到exit命令,脚本会当即终止;终止退出状态取决于exit命令后面的数字

  • 若是未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

子串

[root@shell ~]# test='I am oldboy'
[root@shell ~]# url='www.baidu.com'

${var:n:x}切片

[root@shell ~]# echo ${test:2:2}  # (2,2+2]从第二个字符开始向后两位为止
am
[root@shell ~]# echo $test|awk '{print $2}'
am
[root@shell ~]# echo $test|cut -c3-4
am

${#var}字符长度

[root@shell ~]# echo ${#test}
11
[root@shell ~]# echo $test|wc -L
11
[root@shell ~]# expr length "$test"
11
[root@shell ~]# echo $test|awk '{print length}'
11

统计出字符串小于3的单词 笔试题
I am lzhenya teacher I am 18

[root@shell ~]# cat for.sh
for i in I am lzhenya teacher I am 18
do
		[ ${#i} -lt 3 ] && echo $i
done
[root@shell ~]# sh for.sh
I
am
I
am
18

[root@shell ~]# echo I am lzhenya teacher I am 18|xargs -n1|awk '{if(length<3)print}'
I
am
I
am
18

[root@shell ~]# echo I am lzhenya teacher I am 18|awk '{for(i=1;i<=NF;i++)if(length($i)<3)print $i}'
I
am
I
am
18

删除匹配内容

支持通配符*

${var#}从前日后匹配,${var##}贪婪匹配

${var%}从后往前匹配,${var%%}贪婪匹配

若是要匹配#%,需使用\转义

[root@shell ~]# echo ${url#www.}
baidu.com
[root@shell ~]# echo ${url#*.}
baidu.com
[root@shell ~]# echo ${url#*.*.}
com
[root@shell ~]# echo ${url##*.}
com

[root@shell ~]# echo ${url%.com}
www.baidu
[root@shell ~]# echo ${url%.*}
www.baidu
[root@shell ~]# echo ${url%.*.*}
www
[root@shell ~]# echo ${url%%.*}
www

${var/a/b}替换匹配内容

${var//a/b}贪婪匹配

[root@shell ~]# echo ${url/w/W}
Www.baidu.com
[root@shell ~]# echo ${url//w/W}
WWW.baidu.com
[root@shell ~]# echo ${url/baidu/sina}
www.sina.com
[root@shell ~]# echo $url|sed 's#www#WWW#g'
WWW.baidu.com

ShellScript算术运算

+, -, *, /, %取模(取余), **(乘方),乘法符号有些场景中须要转义

  1. 整数计算使用 expr或者 $[]或者$(())(运算最快)或者 let

  2. 小数计算使用 bc 计算器

  • 实现算术运算:

(1) var=$(expr arg1 arg2 arg3 ...)
(2) var=$[算术表达式]
(3) var=$((算术表达式))
(4) let var=算术表达式
(5) declare –i var = 数值
(6) echo ‘算术表达式’ | bc
  • 随机数

bash有内建的随机数生成器变量:$RANDOM(0-32767)
生成随机数 echo $RANDOM
生成指定范围随机数

示例:
# 生成随机7个数(0-6)
echo $[RANDOM%7]
# 生成随机7个数(31-37)
echo $[$[RANDOM%7]+31]
生成随机字符:cat /dev/urandom
# 生成8个随机大小写字母或数字
cat /dev/urandom |tr -dc [:alnum:] |head -c 8
tr -dc ‘a-zA-Z0-9’</dev/urandom|head -c8 
  • 加强型赋值:

+=, -=, *=, /=, %=

let var OPER value
例如:let count+=3   自加3后自赋值
自增,自减:
let var+=1
let var++
let var-=1
let var--
let var=i++   是赋值后加
let var=++i   是先加后赋值
# 取1-63的余,其中 RANDOM%63 的值是0-62,加1就是1-63
echo $[RANDOM%63+1]
# 生成随机颜色
echo -e  "\033[1;$[RANDOM%7+31]m 字符串\033[0m"
  • 逻辑运算

 true, false
 1, 0
 
 与 &
 1 与 1 = 1
 1 与 0 = 0
 0 与 1 = 0
 0 与 0 = 0
 
 或 |
 1 或 1 = 1
 1 或 0 = 1
 0 或 1 = 1
 0 或 0 = 0
 
 非 !
 ! 1 = 0 ! true
 ! 0 = 1 ! false
 
 短路与 &&
 第一个为0,结果一定为0
 第一个为1,第二个必需要参与运算
 
 短路或 ||
 第一个为1,结果一定为1
 第一个为0,第二个必需要参与运算
 
 异或:^ 异或的两个值,相同为假,不一样为真
 
 短路与和短路或
 [ $RANDOM%6 –eq 0 ] && rm –rf /* || echo “click”
 # 数字互换
 A=10;B=20;A=$[A^B];B=$[A^B];A=$[A^B];echo A=$A B=$B
 
 A=01010=10
 B=10100=20
 A=$[A^B]=11110=30
 
 A=11110=30
 B=10100=20=10
 B=$[A^B]=01010
 
 A=11110=30
 B=01010=10
 A=$[A^B]=10100=20
 
 [root@oldboyedu-lnb ~]# A=10;B=20;A=$[A^B];B=$[A^B];A=$[A^B];echo A=$A B=$B
 A=20 B=10

ShellScript条件测试

非特别说明,则全部文件类操做都会追踪到软连接的源文件

 test EXPRESSION
 [ EXPRESSION ]
 (( EXPRESSION ))   算术表达式
 [[ EXPRESSION ]]   不会发生文件名扩展或者单词分割,会发生参数扩展和命令替换
 注意:EXPRESSION 先后必须有空白字符
test -d "$HOME" ;echo $?
[ "abc" != "def" ];echo $?
test EXPRESSION && echo "exist" || echo "not exist"  更人性化地显示结果
test EXPRESSION && echo true || echo false           更人性化地显示结果

test -e file && echo "exist" || echo "not exist"
test 3 -gt 4 && echo true || echo false
  • bash的数值测试

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

=     是否等于
>     ascii码是否大于ascii码
<     是否小于
!=    是否不等于
=~    左侧字符串是否可以被右侧的 正则表达式 所匹配
注意: 此表达式通常用于[[ ]]中,[[ ]]中匹配正则表达式或通配符,不须要引号

[[ hello == hell? ]] && echo true || echo false
[[ 2\<3 ]] && echo true || echo false
[[ 0 < 1 ]] && echo true || echo false
[[ 2 -lt 3 ]] && echo true || echo false
[ 2 \< 3 ] && echo true || echo false
[ 1 = 1 ] && echo true || echo false

-z "STRING“ 字符串是否为空,空为真,不空为假
-n "STRING“ 字符串是否不空,不空为真,空为假
注意:用于字符串比较时的用到的操做数都应该使用引号
  • bash的文件测试

存在性测试
    -a FILE:同 -e
    -e FILE: 文件存在性测试,存在为真,不然为假
存在性及类别测试
    -b FILE:是否存在且为块设备文件
    -c FILE:是否存在且为字符设备文件
    -d FILE:是否存在且为目录文件
    -f FILE:是否存在且为普通文件
    -h FILE 或 -L FILE:存在且为符号连接文件
    -p FILE:是否存在且为命名管道文件
    -S FILE:是否存在且为套接字文件
  • bash的文件权限测试

文件权限测试:
    -r FILE:是否存在且可读
    -w FILE: 是否存在且可写
    -x FILE: 是否存在且可执行
文件特殊权限测试:
    -u FILE:是否存在且拥有suid权限
    -g FILE:是否存在且拥有sgid权限
    -k FILE:是否存在且拥有sticky权限
  • bash的文件属性测试

文件大小测试:
    -s FILE: 是否存在且非空
文件是否打开:
    -t fd: fd 文件描述符是否在某终端已经打开
    -N FILE:文件自从上一次被读取以后是否被修改过
    -O FILE:当前有效用户是否为文件属主
    -G FILE:当前有效用户是否为文件属组
双目测试:
    FILE1 -ef FILE2: FILE1是不是FILE2的硬连接
    FILE1 -nt FILE2: FILE1是否新于FILE2(mtime)
    FILE1 -ot FILE2: FILE1是否旧于FILE2
  • bash的组合测试条件

第一种方式:
EXPRESSION1 -a EXPRESSION2 而且,只能在test或[]中使用
EXPRESSION1 -o EXPRESSION2 或者,只能在test或[]中使用
! EXPRESSION 非

第二种方式:
COMMAND1 && COMMAND2 而且,短路与,表明条件性的AND THEN,只能在[[]]中使用
COMMAND1 || COMMAND2 或者,短路或,表明条件性的OR ELSE,只能在[[]]中使用
! COMMAND 非
如:[ -f “$FILE” ] && [[ “$FILE”=~ .*\.sh$ ]]
  • 条件性的执行操做符

示例:

grep -q no_such_user /etc/passwd || echo 'No such user'
No such user

ping -c1 -W2 station1 &> /dev/null \
> && echo "station1 is up" \
> || (echo 'station1 is unreachable'; exit 1)
station1 is up

test "$A" = "$B" && echo "Strings are equal"
test “$A”-eq “$B” && echo "Integers are equal“
[ "$A" = "$B" ] && echo "Strings are equal"
[ "$A" -eq "$B" ] && echo "Integers are equal“
[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
# 判断字符串为空或者localhost.localdomain,则临时修改主机名为 www.magedu.com
[ -z “$HOSTNAME” -o $HOSTNAME "=="localhost.localdomain" ] && hostname www.magedu.com  

ShellScript相关命令

read

read           把输入值分配给一个或多个shell变量
     -a        后跟一个变量,该变量会被认为是个数组,而后给其赋值,默认是以空格为分割符
     -e 	   在输入的时候可使用命令补全功能
     -r 	   屏蔽\的转义功能
     -u 	   后面跟fd,从文件描述符中读入,该文件描述符能够是exec新开启的
     -p        指定输入前打印提示信息
     -s        静默输入,输入的字符不在屏幕上显示,通常用于密码
     -n N      指定输入的字符长度最大为N
     -d ‘字符’  以输入的指定‘字符’做为结束符
     -t N      TIMEOUT为N秒,超时退出

read 从标准输入中读取值,给每一个单词分配一个变量,全部剩余单词都被分配给最后一个变量
read -p “Enter a filename: “ FILE

示例: 
修改主机名称为shell,而且修改eth0网卡IP地址为88
[root@shell ~]# cat hostname.sh 
#!/bin/bash
eth0_cfg='/etc/sysconfig/network-scripts/ifcfg-eth0'
old_ip=`ifconfig eth0|awk 'NR==2{print $2}'|awk -F. '{print $NF}'`
read -p "please input hostname: " name
read -p "please input New IP: " IP
hostnamectl set-hostname $name
sed -i "s#$old_ip#$IP#g" $eth0_cfg
grep $IP $eth0_cfg

[root@shell ~]# cat ping.sh 
read -p "Please Input URL: " url
ping -c2 -W1  $url &>/dev/null
[ $? -ne 0 ] && echo "ping不通" || echo "通了"

cat

cat <<EOFcat <<-EOF的区别

用于执行脚本的时候,须要往一个文件里自动输入N行内容。

  • cat用于显示文本文件内容,所有输出。

  • EOF是END Of File的缩写,表示自定义终止符,Ctrl-D就表明EOF。

man说明:

If the redirection operator is <<-, then all leading tab characters are stripped from input lines and the line containing delimiter.

翻译:

若是重定向的操做符是<<-,那么分界符(EOF)所在行的开头部分的制表符(Tab)都将被去除。

也就是说:

  • cat <<EOF中EOF必须顶行写,前面不能用制表符或者空格。若是结束分解符EOF前有制表符或者空格,则EOF不会被当作结束分界符,只会继续被当作stdin来输入。

  • cat <<-EOF中就算最后的EOF前面有多个制表符和空格,但仍然会被当作结束分界符,表示stdin的结束。

trap

trap命令用于指定在接收到信号后将要采起的动做,常见的用途是在脚本程序被中断时完成清理工做。当shell接收到sigspec指定的信号时,arg参数(命令)将会被读取,并被执行,而不会执行原操做。

trap [-lp] [[arg] sigspec ...]
     -l    让shell打印一个命令名称和其相对应的编号的列表
     -p    若是有-p选项而没有提供arg参数,则会打印全部与sigspec指定信号相关联的的trap命令;
           若是没有提供任何参数或者仅有-p选项,trap命令将会打印与每个信号有关联的命令的列表;
     
[arg]参数缺省或者为“-”,每一个接收到的sigspec信号都将会被重置为它们进入shell时的值
[arg]参数是空字符串每个由sigspec指定的信号都会被shell和它所调用的命令忽略;
trap commands signals
# commands 能够是任何有效的Linux命令,或一个用户定义的函数,
# signals 能够是任意数量的信号,或你想来捕获的列表。
# 信号有3种表达方法:信号的数字二、全名SIGINT、缩写INT

参考实例:

trap "rm -f $WORKDIR/work1$ $WORKDIR/dataout$; exit" 1 2  收到指定信号后清理临时文件
trap '' 1 2 20                                            收到指定信号后忽略信号
trap '-' 1 2 20
trap 1 2 20                                               恢复信号的默认操做,重设陷阱

每一个sigspec信号都是是以名字或者编号的形式定义在signal.h头文件中,信号的名字是不区分大小写的,其前缀SIG是可选的,有如下状况:

  1. 若是sigspec是EXIT(0),那么arg指定的命令将会在shell上执行退出命令时执行

  2. 若是sigspec是DEBUG,那么arg指定的命令将会在如下每一个命令执行以前执行:

    简单命令,for语句,case语句,select命令,算法命令,在函数内的第一条命令。

  3. 若是sigspec是ERR,那么arg指定的命令将会在任何简单命名执行完后返回值为非零值时执行,可是也有如下例外状况,arg命令不会执行,这些规则一样适用于errexit选项:

    若是执行失败的命令是紧跟在while或者until关键字以后的一组命令中的一部分时

    若是执行失败的命令是if测试语句的一部分时,是 && 和 ||链接的列表中的一部分时

    若是执行失败的命令的返回值是被取反过的(经过!操做符)

  4. 若是sigspec是RETURN,那么arg指定的命令在每次shell函数或者脚本用"."或者内置的命令执行完成后执行

注意:

  1. 在shell入口处被忽略的命令是无法被trap和reset的。

  2. 被trap的信号,在建立的子进程中使用时会在子进程被建立时被重置为原始的值。

  3. 若是trap使用的sigspec信号是无效的信号,则trap命令返回false(失败),不然返回true(成功)。

① 打印0-9,ctrl+c不能终止

执行脚本后,打印0-9,每秒一个数字,ctrl+c转换为echo press ctrl+c

#!/bin/bash
trap 'echo press ctrl+c' 2
for ((i=0;i<10;i++));do
        sleep 1
        echo $i
done

② 打印0-3,ctrl+c不能终止,3以后恢复,能终止

执行脚本后,打印0-3,每秒一个数字,ctrl+c不能终止,打印3以后解除捕获2信号,能终止

#!/bin/bash
trap '' 2
trap -p
for ((i=0;i<3;i++));do
        sleep 1
        echo $i
done
trap '-' SIGINT
for ((i=3;i<10;i++));do
        sleep 1
        echo $i
done

信号

信号是一种进程间通讯机制,它给应用程序提供一种异步的软件中断,使应用程序有机会接受其余程序活终端发送的命令(即信号)。

应用程序收到信号后,有三种处理方式:忽略,默认,或捕捉。

进程收到一个信号后,会检查对该信号的处理机制:

  1. 若是是SIG_IGN,就忽略该信号;

  2. 若是是SIG_DFT,则会采用系统默认的处理动做,一般是终止进程或忽略该信号;

  3. 若是给该信号指定了一个处理函数(捕捉),则会中断当前进程正在执行的任务,转而去执行该信号的处理函数,返回后再继续执行被中断的任务。

在有些状况下,咱们不但愿本身的shell脚本在运行时刻被中断,好比说咱们写得shell脚本设为某一用户的默认shell,使这一用户进入系统后只能做某一项工做,如数据库备份,咱们不但愿用户使用Ctrl+c之类可以进入到shell状态,作咱们不但愿作的事情。这便用到了信号处理。

常见信号:

1) SIGHUP: 无须关闭进程而让其重读配置文件

2) SIGINT: 停止正在运行的进程;至关于Ctrl+c

3) SIGQUIT: 至关于ctrl+\

9) SIGKILL: 强制杀死正在运行的进程;本信号不能被阻塞,处理和忽略。

15) SIGTERM :终止正在运行的进程(默认为15)

18) SIGCONT :继续运行

19) SIGSTOP :后台休眠

信号名称 信号数 描述
SIGHUP 1 本信号在用户终端链接(正常或非正常)结束时发出, 一般是在终端的控制进程结束时, 通知同一session内的各个做业, 这时它们与控制终端再也不关联。登陆Linux时,系统会分配给登陆用户一个终端(Session)。在这个终端运行的全部程序,包括前台进程组和后台进程组,通常都属于这个Session。当用户退出Linux登陆时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操做为终止进程,所以前台进程组和后台有终端输出的进程就会停止。对于与终端脱离关系的守护进程,这个信号用于通知它从新读取配置文件。
SIGINT 2 程序终止(interrupt)信号, 在用户键入INTR字符(一般是Ctrl+C)时发出。
SIGQUIT 3 和SIGINT相似, 但由QUIT字符(一般是Ctrl+/)来控制。进程在因收到SIGQUIT退出时会产生core文件,在这个意义上相似于一个程序错误信号。
SIGFPE 8 在发生致命的算术运算错误时发出。不只包括浮点运算错误,还包括溢出及除数为0等其它全部的算术的错误。
SIGKILL 9 用来当即结束程序的运行。本信号不能被阻塞,处理和忽略。
SIGALRM 14 时钟定时信号,计算的是实际的时间或时钟时间。 alarm函数使用该信号。
SIGTERM 15 程序结束(terminate)信号,与SIGKILL不一样的是该信号能够被阻塞和处理,一般用来要求程序本身正常退出。shell命令kill缺省产生这个信号。
SIGHUP    1   /* Hangup (POSIX).  */                     终止进程  终端线路挂断
SIGINT    2   /* Interrupt (ANSI).  */                   终止进程  中断进程 Ctrl+C
SIGQUIT   3   /* Quit (POSIX).  */                       创建CORE文件终止进程,而且生成core文件 Ctrl+
SIGILL    4   /* Illegal instruction (ANSI).  */         创建CORE文件,非法指令
SIGTRAP   5   /* Trace trap (POSIX).  */                 创建CORE文件,跟踪自陷
SIGABRT   6   /* Abort (ANSI).  */
SIGIOT    6   /* IOT trap (4.2 BSD).  */                 创建CORE文件,执行I/O自陷
SIGBUS    7   /* BUS error (4.2 BSD).  */                创建CORE文件,总线错误
SIGFPE    8   /* Floating-point exception (ANSI).  */    创建CORE文件,浮点异常
SIGKILL   9   /* Kill, unblockable (POSIX).  */          终止进程  杀死进程
SIGUSR1   10  /* User-defined signal 1 (POSIX).  */      终止进程  用户定义信号1
SIGSEGV   11  /* Segmentation violation (ANSI).  */      创建CORE文件,段非法错误
SIGUSR2   12  /* User-defined signal 2 (POSIX).  */      终止进程  用户定义信号2
SIGPIPE   13  /* Broken pipe (POSIX).  */                终止进程  向一个没有读进程的管道写数据
SIGALARM  14  /* Alarm clock (POSIX).  */                终止进程  计时器到时
SIGTERM   15  /* Termination (ANSI).  */                 终止进程  软件终止信号
SIGSTKFLT 16  /* Stack fault.  */
SIGCHLD   17  /* Child status has changed (POSIX).  */   忽略信号  当子进程中止或退出时通知父进程
SIGCONT   18  /* Continue (POSIX).  */                   忽略信号  继续执行一个中止的进程
SIGSTOP   19  /* Stop, unblockable (POSIX).  */          中止进程  非终端来的中止信号
SIGTSTP   20  /* Keyboard stop (POSIX).  */              中止进程  终端来的中止信号 Ctrl+Z
SIGTTIN   21  /* Background read from tty (POSIX).  */   中止进程  后台进程读终端
SIGTTOU   22  /* Background write to tty (POSIX).  */    中止进程  后台进程写终端
SIGURG    23  /* Urgent condition on socket (4.2 BSD).*/ 忽略信号  I/O紧急信号
SIGXCPU   24  /* CPU limit exceeded (4.2 BSD).  */       终止进程  CPU时限超时
SIGXFSZ   25  /* File size limit exceeded (4.2 BSD).  */ 终止进程  文件长度过长
SIGVTALRM 26  /* Virtual alarm clock (4.2 BSD).  */      终止进程  虚拟计时器到时
SIGPROF   27  /* Profiling alarm clock (4.2 BSD).  */    终止进程  统计分布图用计时器到时
SIGWINCH  28  /* Window size change (4.3 BSD, Sun).  */  忽略信号  窗口大小发生变化
SIGIO     29  /* I/O now possible (4.2 BSD).  */         忽略信号  描述符上能够进行I/O
SIGPWR    30  /* Power failure restart (System V).  */
SIGSYS    31  /* Bad system call.  */

expect

安装

yum install expect -y
expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
       -c:从命令行执行expect脚本,默认expect是交互地执行的
       -d:能够输出输出调试信息  

示例:
expect -c 'expect "\n" {send "pressed enter\n"}
expect -d ssh.exp

expect中相关命令 :

spawn:启动新的进程
send:用于向进程发送字符串
expect:从进程接收字符串
interact:容许用户交互,并停留在远程链接的主机上
exp_continue:匹配多个字符串在执行动做后加此命令

expect最经常使用的语法(tcl语言:模式-动做)

单一分支模式语法:

匹配到hi后,会输出“you said hi”,并换行

expect “hi” {send “You said hi\n"}

多分支模式语法:

匹配hi,hello,bye任意字符串时,执行相应输出。

expect "hi" { send "You said hi\n" } \  
       "hehe" { send "Hehe yourself\n" } \  
       "bye" { send "Good bye\n" }

等同以下:

expect {  
           "hi" { send "You said hi\n"}  
           "hehe" { send "Hehe yourself\n"}  
           "bye" { send  " Good bye\n"}
}

示例

① 用户名密码自动登陆系统

#!/usr/bin/expect
set ip 192.168.7.100
set user root
set password centos
set timeout 10
# 登陆 调用user和ip两个变量的值
spawn ssh $user@$ip
expect {
# 有发现yes/no 输入 yes\n
  "yes/no" { send "yes\n";exp_continue }
# 有发现password输入$password的值
  "password" { send "$password\n" }               
}
# 容许用户交互
interact

② shell调用expect脚本

#!/bin/bash
ip=$1 
user=$2
password=$3
expect <<EOF   # 开启expect命令多行重定向
set timeout 20
spawn ssh $user@$ip
expect {        
        "yes/no" { send "yes\n";exp_continue }        
        "password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "echo centos |passwd --stdin hehe\n" }
expect "]#" { send "exit\n" }
expect eof   # 结束语
EOF

③ 多主机批量操纵:根据相同用户名和密码,批量建立用户

一、建立IP地址清单

[root@oldboyedu-lnb ~]# cat >> iplist.txt << EOF
192.168.7.101
192.168.7.102
192.168.7.103
EOF

二、经过while实现批量读取文件内容

#!/bin/bash
while read ip;do
user=root
password=centos
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {        
        "yes/no" { send "yes\n";exp_continue }        
        "password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }   # 远程ssh登陆后建立用户名
expect "]#" { send "echo centos |passwd --stdin hehe\n" }  # 设置密码
expect "]#" { send "exit\n" }
expect eof
EOF 
done < iplist.txt

④ 多主机批量操纵:根据不一样用户名和密码传递公钥,实现免密钥登陆

一、建立IP地址,密码清单

[root@oldboyedu-lnb ~]# cat >> iplist.txt << EOF
192.168.7.101  wangwang
192.168.7.102  centos
192.168.7.103  hahahaha
EOF

二、经过while实现批量读取文件内容

#!/bin/bash
ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa
while read ip password;do
user=root
set timeout 10
expect << EOF
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect eof
EOF
done <iplist.txt

ShellScript逻辑语句

  • 顺序执行

  • 选择执行

  • 循环执行

条件语句

if

 选择执行:可嵌套
 
 单分支
 if 判断条件;then
    条件为真的分支代码
 fi
 
 双分支
 if 判断条件; then
    条件为真的分支代码
 else
    条件为假的分支代码
 fi
 
 多分支
 if 判断条件1; then
    条件1为真的分支代码
 elif 判断条件2; then
    条件2为真的分支代码
 elif 判断条件3; then
    条件3为真的分支代码
 else
    以上条件都为假的分支代码
 fi
 
 逐条件进行判断,第一次遇为“真”条件时,执行其分支,然后结束整个if语句
 
 Example:
 根据命令的退出状态来执行命令
 if ping -c1 -W2 station1 &> /dev/null; then
     echo 'Station1 is UP'
 elif grep "station1" ~/maintenance.txt &> /dev/null; then
     echo 'Station1 is undergoing maintenance'
 else
     echo 'Station1 is unexpectedly DOWN!'
 exit 1
 fi

① 判断年纪

请输入年纪,先判断输入的是否含有除数字之外的字符,有,输出"please input a int";没有,继续判断是否小于150,是否大于18。

#!/bin/bash
read -p "Please input your age: " age
if [[ $age =~ [^0-9] ]] ;then
    echo "please input a int"
    exit 10
elif [ $age -ge 150 ];then
    echo "your age is wrong"
    exit 20
elif [ $age -gt 18 ];then
    echo "good good work,day day up"
else
    echo "good good study,day day up"
fi

② 判断分数

请输入成绩,先判断输入的是否含有除数字之外的字符,有,输出"please input a int";没有,继续判断是否大于100,是否大于85,是否大于60。

#!/bin/bash
read -p "Please input your score: " score
if [[ $score =~ [^0-9] ]] ;then
    echo "please input a int"
    exit 10
elif [ $score -gt 100 ];then
    echo "Your score is wrong"
    exit 20
elif [ $score -ge 85 ];then
    echo "Your score is very good"
elif [ $score -ge 60 ];then
    echo "Your score is soso"
else
    echo "You are loser"
fi

case

case 变量引用 in
PAT1)
     分支1
     ;;
PAT2)
     分支2
     ;;
*)
     默认分支
     ;;
esac

case支持glob风格的通配符:
*:   任意长度任意字符
?:   任意单个字符
[]: 指定范围内的任意单个字符
a|b: a或b

① 判断yes or no

请输入yes or no,回答Y/y、yes各类大小写组合为yes;回答N/n、No各类大小写组合为no。

#!/bin/bash
read -p "Please input yes or no: " anw
case $anw in
[yY][eE][sS]|[yY])
    echo yes
    ;;
[nN][oO]|[nN])
    echo no
    ;;
*)
    echo false
    ;;
esac

循环语句

for

for (( i = 0; i < 10; i++ )); do
  循环体
done

for item in 列表; do
  循环体
done

执行机制:依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束

列表:

  • 支持glob通配符,如:{1..10}*.sh

  • 也能够引用变量 ${array},如:seq 1 $1

① 求(1+2+...+n)的总和

sum初始值为0,请输入一个数,判断输入的值是否以1-9开头,后面跟任意个0-9的数字,不是,就报错;是,进入for循环,i的范围为1~输入的数,每次的循环为sum=sum+i,循环结束,最后输出sum的值。

#!/bin/bash
sum=0
read -p "Please input a positive integer: " num
if [[ ! $num =~ ^[1-9][0-9]* ]] ;then
    echo "input error"
else
    for i in `seq 1 $num` ;do
        sum=$[$sum+$i]
    done
    echo $sum
fi

while

while [[ 循环控制条件 ]]; do
  循环体
done

while read -r item ;do
  循环体
done < 'file_name'

cat 'file_name' | while read line; do
  循环体
done

循环控制条件;进入循环以前,先作一次判断;每一次循环以后会再次作判断;条件为“true” ,则执行一次循环;直到条件测试状态为“false” 终止循环

遍历文件的每一行:依次读取file_name文件中的每一行,且将行赋值给变量line

① 100之内全部正奇数之和

sum初始值为0,i的初始值为1;当i<=100时,进入循环,判断 i÷2取余,不为0时为奇数,sum=sum+i,i+1;为0时,i+1;循环结束,最后输出sum的值。

#!/bin/bash
sum=0
i=1
while [ $i -le 100 ] ;do
if [ $[$i%2] -ne 0 ];then
    let sum+=i
    let i++
else
    let i++
fi
done
echo "sum is $sum"

② 菜单

#!/bin/bash
cat << EOF
             1)gongbaojiding
             2)kaoya
             3)fotiaoqiang
             4)haishen
             5)baoyu
             6)quit
EOF
while read -p "please choose the number: " num;do
case $num in
    1)
     echo "gongbaojiding price is 30"
     ;;
    2)
     echo "kaoya price price is 80"
     ;;
    3)  
     echo "fotiaoqiang price is 200"
     ;;
    4)
     echo "haishen price is \$20"
     ;;
    5)
     echo "baoyu price is \$10"
     ;;
    6)
     break
     ;;
    *)
    echo "please input again"
esac
done

③ 统计日志访问IP状况

#!/bin/bash
# 其中access_log为访问日志,统计访问IP和次数,导出到文件iplist.txt中
sed -rn 's/^([^[:space:]]+).*/\1/p' access_log |sort |uniq -c > iplist.txt
# while read 逐行处理
while read count ip;do
    if [ $count -gt 100 ];then
        # 将统计后的日志导出到文件crack.log中
        echo from $ip access $count  >> crack.log
    fi
# while read 支持重定向,能够将要统计的文件导入到循环中
done < iplist.txt

④ 统计磁盘使用率大于指定值的信息

#!/bin/bash
# 定义报警的磁盘使用率
WARNING=10
df | awk '{if($5>$WARNING)print $0}'
#!/bin/bash
# 定义报警的磁盘使用率
WARNING=10
df |sed -rn  '/^\/dev\/sd/s#^([^[:space:]]+).* ([[:digit:]]+)%.*$#\1 \2#p' | while read part use; do
    if [ $use -gt $WARNING ]; then
        echo $part will be full,use:$use
    fi
done
#!/bin/bash
# 定义报警的磁盘使用率
WARNING=10
df |awk -F"[[:space:]]+|%" '/dev\/sd/{print $1,$(NF-2)}' > disk.txt
while read part use; do
        if  [ $use -gt $WARNING ]; then
            echo $part will be full,use:$use
        fi
done < disk.txt

until

until [[ 循环控制条件 ]]; do
  循环体
done

进入条件:循环条件为false ;

退出条件:循环条件为true;

恰好和while相反,因此不经常使用,用while就行。

① 监控test用户,登陆就杀死

#!/bin/bash
# 发现test用户登陆,条件为true,退出循环
until pgrep -u test &> /dev/null ;do
    # 每隔0.5秒扫描
    sleep 0.5
done
# 杀死test用户相关进程
pkill -9 -u test

select

select variable in list
do
  循环体
done

① select 循环主要用于建立菜单,按数字顺序排列的示菜单项将显示在标准错误上,并显示PS3提示符,等待用户输入

② 用户输入菜单列表中的某个数字,执行相应的命令

③ 用户输入被保存在内置变量 REPLY 中

④ select 是个无限循环,所以要记住用 break 命令退出循环,或用 exit 命令终止脚本。也能够按 ctrl+c退出循环

⑤ select 常常和 case 联合使用

⑥ 与for循环相似,能够省略 in list, 此时使用位置参量

示例: 生成菜单,并显示选中的价钱

#!/bin/bash
PS3="Please choose the menu: "
select menu in mifan huimian jiaozi babaozhou quit
do
        case $REPLY in
        1|4)
                echo "the price is 15"
                ;;
        2|3)
                echo "the price is 20"
                ;;
        5)
                break
                ;;
        *)
                echo "no the option"
        esac
done

注意:PS3是select的提示符,自动生成菜单,选择5退出循环。

循环控制语句

continue [N]:提早结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层

break [N]:提早结束第N层循环,最内侧为第1层

例:

while CONDTITON1; do
  CMD1
if CONDITION2; then
  continue / break
fi
  CMD2
done

(2)案例:

① 求(1+3+...+49+53+...+100)的和

#!/bin/bash
sum=0
for i in {1..100} ;do
        [ $i -eq 51 ] && continue
        [ $[$i%2] -eq 1 ] && { let sum+=i;let i++; }
done
echo sum=$sum

分析:作1+3+...+99的循环,当i=51时,跳过此次循环,可是继续整个循环,结果为:sum=2449

② 求(1+3+...+49)的和

#!/bin/bash
sum=0
for i in {1..100} ;do
        [ $i -eq 51 ] && break
        [ $[$i%2] -eq 1 ] && { let sum+=i;let i++; }
done
echo sum=$sum

分析:作1+3+...+99的循环,当i=51时,跳出整个循环,结果为:sum=625

shift

shift n    用于将参数列表list左移指定次数,最左端的那个参数就从列表中删除,其后边的参数继续进入循环,n是整数

1.依次读取输入的参数并打印参数个数:
$ cat run.sh
#!/bin/bash
while [ $# != 0 ];do
echo "第一个参数为:$1,参数个数为:$#"
shift
done 
$ sh run.sh a b c d e f 
第一个参数为:a,参数个数为:6
第一个参数为:b,参数个数为:5
第一个参数为:c,参数个数为:4
第一个参数为:d,参数个数为:3
第一个参数为:e,参数个数为:2
第一个参数为:f,参数个数为:1 

2.把参数进行左移3个:
$ cat t.sh
#!/bin/bash
echo -e "./t.sh arg1 arg2 arg3 arg4 arg5 arg6"
str1="${1},${2},${3}"
echo "str1=$str1"
shift 3
str2=$@
echo "str2=$str2"
$ sh t.sh 1 2 3 4 5 6 7
str1=1,2,3

3.将参数从左到右逐个移动:
$ cat shift.sh
#!/bin/bash
while [ $# -ne 0 ]
do
echo "第一个参数为: $1 参数个数为: $#"
shift
done
$ sh shift.sh Lily Lucy Jake Mike
第一个参数为: Lily 参数个数为: 4
第一个参数为: Lucy 参数个数为: 3
第一个参数为: Jake 参数个数为: 2
第一个参数为: Mike 参数个数为: 1

① 建立指定的多个用户

#!/bin/bash
if [ $# -eq 0 ] ;then
        echo "Please input a arg(eg:`basename $0` arg1)"
        exit 1
else
        while [ -n "$1" ];do
                useradd $1 &> /dev/null
                echo "User:$1 is created"
                shift
        done
fi

分析:若是没有输入参数(参数的总数为0),提示错误并退出;反之,进入循环;若第一个参数不为空字符,则建立以第一个参数为名的用户,并移除第一个参数,将紧跟的参数左移做为第一个参数,直到没有第一个参数,退出。

② 打印直角三角形的字符

 #!/bin/bash
 while (( $# > 0 ))
 do
     echo "$*"
    shift
 done
 [root@oldboyedu-lnb ~]# sh trian.sh 1 2 3
 1 2 3
 2 3
 3

Boolean

true

永远成功

false

永远错误


无限循环

while true ;do
  循环体
done
# 或者
until false ;do
  循环体
done

并行循环

每次循环将循环体放入后台执行. 继续下一次循环. 最后等待全部线程执行完毕再退出脚本

for name in 列表 ;do
  {
  循环体
  }&
done
wait

① 搜寻指定ip(子网掩码为24的)的网段中,UP的ip地址

read -p "Please input network (eg:192.168.0.0): " net
echo $net |egrep -o "\<(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>"
[ $? -eq 0 ] || ( echo "input error";exit 10 )
IP=`echo $net |egrep -o "^([0-9]{1,3}\.){3}"`
for i in {1..254};do
        {
        ping -c 1 -w 1 $IP$i &> /dev/null && \
        echo "$IP$i is up"
        }&
done
wait

分析:请输入一个IP地址,例如192.168.37.234,若是格式不是0.0.0.0 则报错退出;正确则进入循环,IP变量的值为192.168.37. i的范围为1-254,并行ping 192.168.37.1-154,ping通就输出此IP为UP。

函数递归示例

函数递归: 函数直接或间接调用自身 注意递归层数 递归实例: 阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语,一个正整数的阶乘(factorial)是全部小于及等于该数的正整数的积,而且有0的阶乘为1,天然数n的阶乘写做n!

n!=1×2×3×...×n
阶乘亦能够递归方式定义:0!=1,n!=(n-1)!×n
n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!

函数递归示例
示例:fact.sh
#!/bin/bash

fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1

展开命令行

  • 把命令行分红单个命令词

  • 展开别名

  • 展开大括号的声明({})

  • 展开波浪符声明(~)

  • 命令替换$()和``

  • 再次把命令行分红命令词

  • 展开文件通配(*、?、[abc]等等)

  • 准备I/0重导向(<、>)

  • 运行命令

防止扩展

  1. 反斜线\会使随后的一个字符按原意解释

  2. 单引号'防止全部扩展

  3. 双引号"除了如下状况,防止全部扩展:

  $   (美圆符号)   变量扩展(注意:"$" 输出 $,仍有特殊含义)
  `   (反引号)      命令替换
  \   (反斜线)     禁止单个字符扩展
  !   (叹号)       历史命令替换

分支

HEAD_KEYWORD parameters; BODY_BEGIN
  BODY_COMMANDS
BODY_END
  • 将HEAD_KEYWORD和初始化命令或者参数放在第一行;

  • 将BODY_BEGIN一样放在第一行;

  • 复合命令中的BODY_COMMANDS部分以2个空格缩进;

  • BODY_END部分独立一行放在最后;

  1. if

if [[ condition ]]; then
  # statements
fi

if [[ condition ]]; then
  # statements
else
  # statements
fi

if [[ condition ]]; then
  # statements
elif [[ condition ]]; then
  # statements
else
  # statements
fi
  • if 后面的判断 使用 双中括号[[]]

  • if [[ condition ]]; then 写在一行

  1. while

while [[ condition ]]; do
  # statements
done

while read -r item ;do
  # statements
done < 'file_name'
  1. until

until [[ condition ]]; do
  # statements
done
  1. for

for (( i = 0; i < 10; i++ )); do
  # statements
done

for item in ${array}; do
  # statements
done
  1. case

case $var in
  pattern )
    #statements
    ;;
    *)
    #statements
    ;;
esac

ShellScript函数

function用法

  1. 函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。

  2. 它与shell程序形式上是类似的,不一样的是它不是一个单独的进程,不能独立运 行,而是shell程序的一部分,定义函数只对当前的会话窗口有效,若是再打开一个窗口再定义另一个函数,就对另外一个窗口有效,二者互不影响。

  3. 函数和shell程序比较类似,区别在于如下两种:

(1)Shell程序在子Shell中运行。

(2)而Shell函数在当前Shell中运行。所以在当前Shell中,函数能够对shell中变量进行修改。

定义函数

function main(){
  #函数执行的操做
  #函数的返回结果
}

main(){
  #函数执行的操做
  #函数的返回结果
}

function main {
  #函数执行的操做
  #函数的返回结果
}
  1. 使用关键字 function 显示定义的函数为 public 的函数,能够供外部脚本以 sh 脚本 函数 函数入参 的形式调用

  2. 未使用关键字 function 显示定义的函数为 privat 的函数, 仅供本脚本内部调用,注意这种privat是人为规定的,并非shell的语法,不推荐以 sh 脚本 函数 函数入参 的形式调用,注意是不推荐而不是不能。

本shell规约这样作的目的就在于使脚本具备必定的封装性,看到 function 修饰的就知道这个函数能被外部调用, 没有被修饰的函数就仅供内部调用。你就知道若是你修改了改函数的影响范围. 若是是被function修饰的函数, 修改后可能影响到外部调用他的脚本, 而修改未被function修饰的函数的时候,仅仅影响本文件中其余函数。

如 core.sh 脚本内容以下是

# 从新设置DNS地址 []<-()
function set_name_server(){
  > /etc/resolv.conf
  echo nameserver 114.114.114.114 >> /etc/resolv.conf
  echo nameserver 8.8.8.8 >> /etc/resolv.conf
  cat /etc/resolv.conf
}
# info级别的日志 []<-(msg:String)
log_info(){
  echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[32m [info] \033[0m $*" >&2
}
# error级别的日志 []<-(msg:String)
log_error(){
  # todo [error]用红色显示
  echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[31m [error] \033[0m $*" >&2
}

则我可使用 sh core.sh set_name_server 的形式调用 set_name_server 函数,但就不推荐使用 sh core.sh log_info "Hello World" 的形式使用 log_infolog_error 函数,注意是不推荐不是不能。


(1)可在交互式环境下定义函数

(2)可将函数放在脚本文件中做为它的一部分

#!/bin/bash
# 定义function func_os_version,在大括号里边定义命令,取出操做系统的版本号,相似于定义别名同样
func_os_version () {  
sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/redhat-release   
}
# 直接写上函数名,或者用echo加反引号输出结果
echo OS version is `func_os_version`  

若是命令过多,不太方便

(3)可放在只包含函数的单独文件中

  • 而后将函数文件载入shell

  • 文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main

  • 一旦函数文件载入shell,就能够在命令行或脚本中调用函数。可使用set查看全部定义的函数,其输出列表包括已经载入shell的全部函数

  • 若要改动函数,首先用unset function_name从shell中删除函数。改动完毕后,再从新载入此文件

 # 将定义的函数放到functions文件中
 [root@centos-7 ~]# cat functions
 func_os_version () {
 sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/redhat-release
 }
 
 [root@centos-7 ~]# cat osversion.sh  
 #!/bin/bash
 # 在当前脚本加载 functions
 source functions
 # 调用函数
 func_os_version
 
 # 对脚本osversion.sh加上执行权限
 [root@centos-7 ~]# chmod +x osversion.sh
 # 运行脚本
 [root@centos-7 ~]# ./osversion.sh
 7

可使用declare -F 查看全部定义的函数

定义环境函数

使子进程也可以使用

(1)声明:export -f function_name

(2)查看:export -fdeclare -xf

调用函数

调用:给定函数名

函数名出现的地方,会被自动替换为函数代码

函数的生命周期:被调用时建立,返回时终止


使用脚本单独调用函数中的某个函数

#!/usr/bin/env bash
# shellcheck disable=SC1091,SC2155
readonly local TRUE=0 && readonly local FALSE=1
# 脚本使用帮助文档
manual(){
  cat "$0"|grep -v "less \"\$0\"" \
          |grep -B1 "function "   \
          |grep -v "\\--"         \
          |sed "s/function //g"   \
          |sed "s/(){//g"         \
          |sed "s/#//g"           \
          |sed 'N;s/\n/ /'        \
          |column -t              \
          |awk '{print $1,$3,$2}' \
          |column -t
}
######################################################################
# 主函数
main(){
  (manual)
}
######################################################################
# 执行函数 [Any]<-(function_name:String,function_parameters:List<Any>)
execute(){
  function_name=$1
  shift # 参数列表以空格为分割左移一位,至关于丢弃掉第一个参数
  function_parameters=$*
  (${function_name} "${function_parameters}")
}
case $1 in
  "-h" | "--help" | "?") (manual);;
  "") (main) ;;
  *) (execute "$@") ;;
esac

使用如上的框架,只须要在 两个 ###################################################################### 之间写函数,就可使用 sh 脚本名称 脚本中的某个函数 脚本中的某个函数的入参 的形式调用函数了。 使用 sh 脚本名称 ? 或者 sh 脚本名称 -h/--help 就能够查看这个脚本中的函数说明了。

img

  1. 在函数内部首先使用有意义的变量名接受参数,而后在使用这些变量进行操做,禁止直接操做$1,$2 等,除非这些变量只用一次

  2. 函数的注释 函数类型的概念是从函数编程语言中的概念偷过来的,shell函数的函数类型指的是函数的输入到函数的输入的映射关系

# 主函数 []<-()                  <-------函数注释这样写
function main(){
  local var="Hello World!!!"
  echo ${var}
}
# info级别的日志 []<-(msg:String)  <-------带入参的函数注释
log_info(){
  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: [info] $*" >&2
}

main函数的函数类型是 []<-() , <- 左侧表的是函数的返回值类型用[]包裹, 右侧是函数的参数类型用()包裹,多个参数用 ',' 分隔, 参数的描述是从 Scala 语言中偷过来, 先是参数名称, 而后是参数类型, 中间用:分隔

对于main函数的注释来讲, # 顶格写,后面紧跟一个空格,其实这样写是遵循的markdown的语法, 后面再跟一个空格,而后是 []<-(),表明这个函数没有入参也没有返回值,这个函数的目的就是执行这个这个函数中的命令,但我不关心这个函数的返回值。也就是利用函数的反作用来完成咱们想要的操做。

对于log_info也是同样不过最后的函数类型是 []<-(msg:String) 表明入参是一个string类型的信息,而后也没有返回值。 关于函数的返回值,我理解的函数的返回值有两种形式,一种是显示的return一种是隐式的echo

如下是几种常见的写法

[]<-()
[String]<-(var1:String,var2:String)
[Boolean]<-(var1:String,var2:Int)
[]<-(var1:String)

返回值

一、函数的执行结果返回值:

(1) 使用echo等命令进行输出

(2) 函数体中调用命令的输出结果

二、函数的退出状态码:

(1) 默认取决于函数中执行的最后一条命令的退出状态码

(2) 自定义退出状态码,其格式为:

return 从函数中返回,用最后状态命令决定返回值:

  (1)return 0 无错误返回。

  (2)return 1-255 有错误返回


执行一条命令的时候, 好比 pwd 正常状况下它输出的结果是 当前所处的目录

$ pwd
/Users/chenshang

shell中必然有一种状态来标识一条命令是否执行成功,也就是命令执行结果的状态。

0表明真、成功的含义。

非零表明假、失败的含义。

因此 pwd 这条命令若是执行成功的话,命令的执行结果状态必定是0,而后返回值才是当前目录。若是这条命令执行失败的话,命令的执行结果状态必定不是0,有多是1 表明命令不存在,而后输出 not found,也有可能执行结果状态是2表明超时,而后什么也不输出。那怎么获取这个命令的执行结果和执行结果的状态呢?

function main(){
  pwd
}

执行main函数就会在控制台输出当前目录 若是想要将pwd的内容获取到变量中以供后续使用呢

function main(){
  local dir=$(pwd)
  echo "dir is ${dir}"
}

若是想要获取pwd的执行结果的状态呢

function main(){
  local dir=$(pwd)
  local status=$?
  echo "pwd run status is ${status}" #这个stauts必定有值,且是int类型,取值范围在0-255之间
  echo "dir is ${dir}"
}

显示return

return 用来显示的返回函数的返回结果,例如

# 检查当前系统版本 [Integer]<-()
function check_version(){
  (log_info "check_version ...") # log_info是我写的工具类中的一个函数
  local version # 这里是先定义变量,在对变量进行赋值,咱们每每是直接初始化,而不是像这样先定义在赋值,这里只是告诉你们能够这么用
  version=$(sed -r 's/.* ([0-9]+)\..*/\1/' /etc/redhat-release)
  (log_info "centos version is ${version}")
  return "${version}"
}

这样这个函数的返回值是一个数值类型,我在脚本的任何地方调用check_version这个函数后,使用 $? 获取返回值

check_version
local version=$?
echo "${version}"

注意这里不用 local version=$(check_version) 这种形式获取结果,这样也是获取不到结果的,由于显示的return结果,返回值只能是[0-255]的数值,这对于咱们通常的函数来讲就足够了,由于咱们使用显示return的时候每每是知道返回结果必定是数字且在[0-255]之间的,经常用在状态判断的时候。

本shell规约规定:

  1. 明确返回结果是在[0-255]之间的数值类型的时候使用显示 reuturn 返回结果

  2. 返回结果类型是Boolean类型,也就是说函数的功能是起判断做用,返回结果是真或者假的时候使用显示 return 返回结果

# 检查网络 [Boolean]<-()
function check_network(){
  (log_info "check_network ...")
  for((i=1;i<=3;i++));do
    http_code=$(curl -I -m 10 -o /dev/null -s -w %\{http_code\}  www.baidu.com)
    if [[ ${http_code} -eq 200 ]];then
      (log_info "network is ok")
      return ${TRUE}
    fi
  done
  (log_error "network is not ok")
  return ${FALSE}
}
# 获取数组中指定元素的下标 [int]<-(TABLE:Array,target:String)
function get_index_of(){
  readonly local array=($1)
  local target=$2
  local index=-1 # -1实际上是255
  local size=${#array[@]}
  for ((i=0;i<${size};i++));do
    if [[ ${array[i]} == ${target} ]];then
      return ${i}
    fi
  done
  return ${index}
}

隐式echo

return 用来显示的返回函数的返回结果,例如

# 将json字符串格式化树形结构 [String]<-(json_string:String)
function json_format(){
  local json_string=$1
  echo "${json_string}"|jq . #jq是shell中处理json的一个工具
}

函数中全部的echo照理都应该输出到控制台上 例如

json_format "{\"1\":\"one\"}"

你会在控制台上看到以下输出

{
  "1": "one"
}

可是一旦你用变量接收函数的返回值,这些本该输出到控制台的结果就都会存储到你定义的变量中 例如

json=$(json_format "{\"1\":\"one\"}")
echo "${json}" # 若是没有这句,上面的语句执行完成后,不会在控制台有任何的输出

咱们把 json_format 改造一下

# 将json字符串格式化树形结构 [String]<-(json_string:String)
function json_format(){
  local json_string=$1
  echo "为格式化以前:${json_string}" # 其实新添加的只一句只是用来记录一行日志的
  echo "${json_string}"|jq . # jq是shell中处理json的一个工具
}

echo "为格式化以前:${json_string}" 其实新添加的只一句只是用来记录一行日志的,可是json=$(json_format "{"1":"one"}") json 也会将这句话做为返回结果进行接收,但这是我不想要看到的。

子shell能够捕获父shell的变量,但不能改变父shell的变量,使用()将代码块包裹,包裹的代码块将在子shell中运行,子shell至关于独立的一个环境,不会影响到父shell的结果

因此若是我不想让 echo "为格式化以前:${json_string}" 这句话也做为结果的话,我就只须要用()将代码块包裹便可

# 将json字符串格式化树形结构 [String]<-(json_string:String)
function json_format(){
  local json_string=$1
  (echo "为格式化以前:${json_string}") # 其实新添加的只一句只是用来记录一行日志的
  echo "${json_string}"|jq . # jq是shell中处理json的一个工具
}

示例

① 对不一样的成绩分段进行判断

[root@oldboyedu-lnb ~]# cat functions
func_is_digit(){
    # 判断参数$1不是空,就为真,取反,空为真
    if [ ! "$1" ];then
        # 请输入数字
        echo "Usage:func_is_digit number"
        return  10
    # 若是输入是数字,返回0
    elif [[ $1 =~ ^[[:digit:]]+$ ]];then
        return 0
    else
    # 不然提醒不是数字
        echo "Not a  digit"  
        return 1
    fi
}
 [root@oldboyedu-lnb ~]# cat score.sh
 #!/bin/bash
 source /data/functions
 read -p "Input your score:" SCORE
 func_is_digit $SCORE
 #判断上面的命令执行结果不等于0就退出
 if [ $? -ne 0 ];then
     exit
 else
     # 若是成功了,对成绩的三种判断以下。
     if [ $SCORE -lt 60 ];then
         echo "You are loser"
     elif [ $SCORE -lt 80 ];then
         echo "soso"
     else
         echo "very good"
     fi
 fi

② function配合case:代码发布与回滚与检验

 #!/bin/bash
 # Author: liupengju
 # date:   2020-06-22
 # TEL:     xxxxxxxxxx
 # 代码发布与回滚
 set -e
 set -u
 
 # adx代码部署 定义变量
 ADX_DIR=/gnome/adx
 adx_new_version="gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar"
 ADX_NEW_MD5=`md5sum $adx_new_version | awk '{ print $1 }'`
 
 # adx代码部署 cf平台的md5码
 ADX_CHK_MD5="43bcfe7594f083a8653126e0896b93ac"
 
 # directAd代码部署 定义变量
 direct_DIR=/gnome/directAd/
 direct_version="direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar"
 direct_MD5=`md5sum $direct_version | awk '{ print $1 }'`
 #direct_old_version=$(ls -l |tail -n1 | awk '{print $9}')
 
 # directAd代码部署 cf平台的md5码
 direct_CHK_MD5="03c3c2fc62b2edfc92e548351010ee9f"
 
 ##########部署directAd代码#############################
 fun_copy_direct_code(){
     mv $direct_DIR/$direct_version  $direct_DIR/bak/${direct_version}_$(date +"%F-%T")
         echo "-----上一个版本已经移动到备份目录"
     cp /data/$direct_version  $direct_DIR && echo "-----代码复制成功!!!"
 }
 
 fun_chk_direct_code(){
       if [[ "$direct_MD5" == "$direct_CHK_MD5" ]];then
            echo "-----代码校验成功" && echo "代码部署成功后MD5值为:$direct_MD5"
       else
            echo "-----代码校验失败" && exit
       fi
 }
 
 fun_deploy_direct_restart(){
     #$direct_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "后端服务重启成功!!!"
 }
 
 # 验证端口存活状态
 fun_chk_direct_port1(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
      for port in $PORT1 $PORT2;do
         echo "The port is:$port------监听端口正常"
      done
 }
 
 #############回滚directAd代码###################################
 fun_rollback_direct_code(){
     cd $direct_DIR/bak
 # 提取上一个版本的jar包
     direct_old_version=$(ls -l |tail -n1 | awk '{print $9}')  
     mv $direct_DIR/${direct_version} $direct_DIR/bak/${direct_version}_$(date +"%F-%T")
     mv $direct_DIR/bak/${direct_old_version} $direct_DIR/${direct_version}
     echo "------旧版本代码移动成功"
     direct_old_MD5=$(md5sum $direct_DIR/${direct_version} | awk '{print $1}')
     echo "代码回滚后MD5值为:$direct_old_MD5"
 }
 
 fun_rollback_direct_restart(){
     #$direct_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "--------后端服务重启成功"
 }
 
 # 验证端口存活状态
 fun_chk_direct_port2(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
     for port in $PORT1 $PORT2;do
         echo "The port is:$port------端口监听正常"
     done
 }
 
 #####################adx代码部署########################################
 fun_copy_adx__code(){
     mv $ADX_DIR/$adx_new_version  $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T")
         echo "-----上一个版本已经移动到备份目录"
     cp /data/$adx_new_version  $ADX_DIR && echo "-----代码复制成功!!!"
 }
 
 fun_chk_adx_code(){
     if [[ "$ADX_NEW_MD5" == "$ADX_CHK_MD5" ]];then
         echo "-----代码校验成功" && echo "代码部署成功后MD5值为:$ADX_NEW_MD5"
     else
             echo "-----代码校验失败" && exit
     fi
 }
 
 fun_deploy_adx_restart(){
     #$ADX_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "后端服务已经启动!!!"
 }
 
 # 验证端口存活状态
 fun_chk_adx_port1(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
       for port in $PORT1 $PORT2;do
           echo "The port is:$port------监听的端口正常启动"
       done
 }
 
 ###################################adx代码回滚###########################
 fun_rollback_adx_code(){
     cd $ADX_DIR/bak
     adx_old_version=$(ls -l |tail -n1 | awk '{print $9}')
     mv $ADX_DIR/${adx_new_version} $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T")
     mv $ADX_DIR/bak/${adx_old_version} $ADX_DIR/${adx_new_version}
     echo "------旧版本代码移动成功"
     adx_old_MD5=$(md5sum $ADX_DIR/${adx_new_version} | awk '{print $1}')
     echo "代码回滚后MD5值为:$adx_old_MD5"
 }
 
 fun_rollback_adx_restart(){
     #$ADX_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "--------后端服务已经启动"
 }
 
 # 验证端口存活状态
 fun_chk_adx_port2(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
     for port in $PORT1 $PORT2;do
         echo "The port is:$port-------端口监听正常"
     done
 }
 
 case $1 in
  direct_deploy)
      fun_copy_direct_code
      fun_chk_direct_code
      fun_deploy_direct_restart
      fun_chk_direct_port1
      ;;
  direct_rollback)
      fun_rollback_direct_code
      fun_rollback_direct_restart
      fun_chk_direct_port2
      ;;
  adx_deploy)
      fun_copy_adx__code
      fun_chk_adx_code
      fun_deploy_adx_restart
      fun_chk_adx_port1
      ;;
  adx_rollback)
    fun_rollback_adx_code
    fun_rollback_adx_restart
    fun_chk_adx_port2
      ;;
 esac
#!/bin/bash
# Auth:   liupenghui
# date:   2020-10-22
# TEL:    xxxxx
# 部署完成校验

####验证adserver版本号#############
fun_chk_adx_version(){
    ansible adx -m shell -a 'md5sum   /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |head -n62 |tee version_adx |cat -n
    adx_version=$(ansible adx -m shell -a 'md5sum /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}')
    echo -e "\e[1;32m新发布的版本号为:$adx_version\e[0m"
    version1=$(diff metadata  version_adx)
        if [ -z $version1 ];then
           echo -e "\e[1;32m代码部署成功 \e[0m"
    else
           echo -e "\e[1;31m请检查错误  \e[0m"
    fi
}
 
####验证directAd版本号############
fun_chk_direct_version(){  
        ansible adx -m shell -a ' md5sum   /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |head -n62 |tee version_direct |cat -n
        direct_version=$(ansible adx -m shell -a 'md5sum /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}')
        echo -e "\e[1;32m新发布的版本号为:$direct_version\e[0m"
        version2=$(diff metadata  version_direct)
        if [ -z $version2 ];then
           echo -e "\e[1;32m代码部署成功 \e[0m"
        else
           echo -e "\e[1;31m请检查错误  \e[0m"
        fi
}
 
###验证8080端口状态###############
fun_chk_8080_port(){
    chk_ip_8080=$(ansible adx -m shell -a ' netstat -ntulp |grep 8080' |awk '{print $1}' |egrep "[0-9]+\.*" |sort | tee data_8080.bak |cat -n)
    DIR_8080=$(diff metadata  data_8080.bak)
    if [ -z $DIR_8080 ];then
           echo -e "\e[1;32m端口检查成功,端口号:8080 \e[0m"
    else
           echo -e "\e[1;31m请检查错误  \e[0m"
    fi
}
 
####验证8182端口状态#############
fun_chk_8182_port(){
    chk_ip_8182=$(ansible adx -m shell -a ' netstat -ntulp |grep 8182' |awk '{print $1}' |egrep "[0-9]+\.*" |sort |tee data_8182.bak |cat -n)
    DIR_8182=$(diff metadata  data_8182.bak)
    if [ -z $DIR_8182 ];then
        echo -e "\e[1;32m端口检查成功,端口号:8182 \e[0m"
    else
        echo -e "\e[1;31m请检查错误  \e[0m"
    fi
}
 
case $1 in
     adx)
       fun_chk_adx_version
       fun_chk_8080_port
       fun_chk_8182_port
       ;;
     direct)
       fun_chk_direct_version
       fun_chk_8080_port
       fun_chk_8182_port
       ;;
esac

环境变量

bash配置文件

  • 按生效范围划分,存在两类:

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

按功能划分,存在两类:

profile类:
为交互式登陆的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
我的:~/.bash_profile
功用:
(1) 用于定义环境变量
(2) 运行命令或脚本
bashrc类:
为非交互式和交互式登陆的shell提供配置
全局:/etc/bashrc
我的:~/.bashrc
功用:
(1) 定义命令别名和函数
(2) 定义本地变量

注意:

(1)命令中定义的特性,譬如变量和别名,仅对当前shell进程有效

(2)配置文件中定义的特性,只对随后新启动的shell进程有效,想让经过配置文件定义的特性当即生效,能够在命令行重复定义一次,或者让shell进程重读配置文件

  • source /PATH/FROM/CONF_FILE

  • . /PATH/FROM/CONF_FILE

  • 用户退出任务~/.bash_logout

    在退出登陆shell时运行。

    用于

    • 建立自动备份

    • 清除临时文件

示例:当退出登陆时,删除文件/data/test

[root@centos7 ~]# vim ~/.bash_logout
# ~/.bash_logout
rm -rf /data/test

环境变量配置文件

系统开机启动,环境变量配置文件的执行顺序为: /etc/profile -> (~/.bash_profile | ~/.bash_login | ~/.profile) -> ~/.bashrc -> /etc/bashrc -> ~/.bash_logout

 (1)/etc/profile:
 此文件为系统的每一个用户设置环境信息,当用户第一次登陆时,该文件被执行.
 并从/etc/profile.d目录的配置文件中搜集shell的设置(*.sh文件)。
 (2)~/.bash_profile:
 每一个用户均可使用该文件输入专用于本身使用的shell信息,当用户登陆时,该文件仅仅执行一次!
 默认状况下,他设置一些环境变量,执行用户的.bashrc文件。
 (3)~/.bashrc:
 该文件包含专用于你的bash shell的bash信息,当登陆时以及每次打开新的shell时,
 该文件被读取,调用/etc/bashrc。
 (4)/etc/bashrc:
 为每个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取。
 (5)~/.bash_logout:
 当每次退出系统(退出bash shell)时,执行该文件。可用于 建立自动备份 和 清除临时文件
 注意:/etc/profile 中设定的变量(全局)的能够做用于任何用户,
      而~/.bashrc 等中设定的变量(局部)只能继承/etc/profile中的变量,他们是"父子"关系.
  ~/.bash_profile 是交互式 login 方式进入 bash 时运行的;
      ~/.bashrc 是交互式 non-login 方式进入 bash 时运行的;
  一般两者设置大体相同,一般前者会调用后者,以便统一配置用户环境。

其余配置文件

~/.bash_history 
是bash shell的历史记录文件,里面记录了你在bash shell中输入的全部命令。
环境变量:
HISSIZE  设置在历史记录文件里保存记录的条数。
/etc/enviroment 是系统的环境变量,与登陆用户无关,例如登陆提示语言,不必修改
/etc/profile    是全部用户的环境变量,与登陆用户有关,变量冲突以用户的环境变量为准
系统应用程序的执行与用户环境能够是无关的,但与系统环境是相关的.

系统开机时,读取的顺序是:?
/etc/enviroment -> /etc/profile
登录系统时,用户shell创建环境,读取的顺序是:
/etc/profile -> /etc/enviroment 

永久修改环境变量(编辑环境变量配置文件)

示例: 在文件末尾添加环境变量: export PATH=$PATH:/opt/lamp/mysql/bin export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib

使环境变量生效

source /etc/profile   通知bash进程从新读取配置文件
. /etc/profile        执行配置文件
注销系统

(同名的环境变量,后写入的起做用)

查看环境变量

set                  显示当前shell的变量,包括当前用户的变量(内部命令)
export               显示当前导出成用户变量的shell变量(内部命令)
env                  显示当前用户的环境变量(外部命令)
declare -x
printenv

echo $PATH  显示当前导出成用户变量shell的变量,包括当前用户的变量中的PATH

临时设置或显示环境变量

注意:使用export或declare设置的变量都是临时变量,也就是说退出当前的shell,为该变量定义的值便不会生效

export [-fnp] [NAME]=[变量设置值]    当前导出成用户变量的shell变量
       -f                 表明[NAME]中为函数名称。
       -n                 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
       -p                 列出全部的shell赋予程序的环境变量。
Examples:
export MYENV=7             # 定义环境变量并赋值
export LANG=zh_CN.UTF-8    # 临时修改语言环境字符集中文
declare [-fFirx] [-p] [name[=value]]    显示全部shell变量(与 typeset 相同)
	    +/-       "-"可用来指定变量的属性,"+"则是取消变量所设的属性;
		  -p      将显示每一个[name]的属性和值    
          -f      仅显示函数;
          -r      将[name]设置为只读;
          -x      指定的变量会成为用户环境变量,可供shell之外的程序来使用;env |grep [name]
          -i      [value]能够是数值,字符串或运算式。
		  -F      包含-f,禁止显示函数定义;只有函数名和属性会被显示
Examples:
declare -x MYENV=7                   # 定义环境变量并赋值
declare -i number=$RANDOM*100/32767  # 设置0~100之间的随机数变量number

显示和设置shell中的行为选项:shopt(向下兼容set)

shopt    列出当前shell中只能由shopt设置的选项
         cdspell       自动改正cd命令参数中的小错误
         hostcomplete  以@开头时,按tab键可进行主机名的自动完成
         dotgblob      以点开始的文件名被包含在路径名扩展中
         mailwarn      显示邮件警告信息
shopt -o 列出可由set命令设置的选项
         emacs         进入emacs编辑模式
         vi            进入vi编辑模式
         ignoreeof     不容许单独使用Ctrl+D退出的用法,要使用exit。与IGNOREEOF=10等价
         noclobber     不容许重定向覆盖已存在文件
         noglob        不容许扩展文件名通配符
         nounset       使用未定义的变量时给出错误
      -p 显示可设置选项及当前取值
      -s 设置每一选项为on
      -u 设置每一选项为off
      -q 不输出信息

临时设置shell变量:set

set               显示全部shell变量
    -a            标示已修改的变量,导出至环境变量。echo $[name]
    -b            使被停止的后台程序马上回报执行状态。
    -C            重定向所产生的文件没法覆盖已存在的文件。
    -d            Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。
    -e            若指令传回值不等于0,则当即退出shell。等同 set –o errexit
    -f            取消使用通配符。
    -h            自动记录函数命令的所在位置。hash下来
    -H,--history  可利用"!"加<指令编号>的方式来执行history中记录的指令。
    -k            指令所给的参数都会被视为此指令的环境变量。
    -l            记录for循环的变量名称。
    -m            使用监视模式。能够经过Job control来控制进程的中止、继续,后台或者前台执行等。
    -n            只读取指令,而不实际执行。
    -p            启动优先顺序模式。
    -P            启动-P参数后,执行指令时,会以实际的文件或目录来取代符号链接。
    -t            执行完随后的指令,即退出shell。
    -u            当执行时使用到未定义过的变量,则显示错误信息。等同 set –o nounset
    -v            显示shell所读取的输入值。
    -x            执行指令后,会先显示该指令及所下的参数
    -i,--interactive-comments  交互式 shell ,shell 脚本中默认关闭
    -B,braceexpand             大括号扩展命令,默认开启

删除shell变量:unset

unset [-f] [-v] [name ...]  删除shell变量 name
      -f                    仅删除函数
      -v                    仅删除变量

经常使用的环境变量

PATH

决定了shell将到哪些目录中寻找命令或程序,预设可执行文件或命令的搜索路径。
$PATH=路径1:路径2:...:路径n

RANDOM

0~32767之间的随机整数

HISTSIZE

设置在历史记录文件里保存记录的条数。

HISTFILE

指定历史文件,默认为~/.bash_history

HISTFILESIZE

命令历史文件记录的条数

HISTTIMEFORMAT

HISTTIMEFORMAT=“%F %T “ 显示时间

HISTIGNORE

HISTIGNORE=“str1:str2*:… “ 忽略str1str2开头的历史

HISTCONTROL

HISTCONTROL=            记录方式
            ignoredups  默认,忽略重复的命令,连续且相同为“重复”
            ignorespace 忽略全部以空白开头的命令
            ignoreboth  至关于 ignoredups,ignorespace 的组合
            erasedups   删除重复命令

CDPATH

cd 相对路径进入目录时,寻找匹配目录的目录

PWD

当前目录路径,自动更新

OLDPWD

上一次目录路径,自动更新

LANG

系统语言

LANG="en_US.UTF-8"
LANG="zh_CN.UTF-8"
Cent OS 6.x 配置文件
/etc/sysconfig/i18n
Cent OS 7.x 配置文件:
/etc/locale.conf
localectl set-locale LANG='en_US.UTF-8'

PS1

提示符变量,用于设置提示符格式,设置一级shell提示符环境变量,个性化终端命令行提示符的信息或格式。

PS1 基本提示符 例如:export PS1="[\u@\h \w]\$ "
    \d :表明日期,格式为weekday month date,例如:Wed Dec 12
    \H :完整的主机名称。例如:hostname是debian.linux
    \h :仅取主机的第一个名字,如上例,则为debian,.linux则被省略
    \t :显示时间为24小时格式,如:HH:MM:SS
    \T :显示时间为12小时格式
    \A :显示时间为24小时格式:HH:MM
    \u :当前用户的帐号名称 如:root
    \v :BASH的版本信息  如:3.2
    \w :完整的工做目录名称。家目录会以~代替 如显示/etc/default/
    \W :利用basename取得工做目录名称,只会列出最后一个目录。如上例则只显示default
    \# :下达的第几个命令
    \$ :提示字符,若是是root时,提示符为:# ,普通用户则为:$ 

PS2

用于设置二级shell提示符环境变量。

终端命令行的相关设置:stty

stty     显示终端命令行的相关设置
     -a  打印终端全部当前设置
	    iuclc              禁止输出大写,前加-开启
		olcuc              禁止输出小写,前加-开启
		size               打印出终端的行数和列数
		stty eof "string"  修改Ctrl+D快捷键
		echo               打开回显,前加-关闭
		igncr              忽略回车符,前加-关闭

LOGNAME:当前用户的登陆名 LANGUGE:语言相关的环境变量,多语言能够修改此环境变量 MAIL:当前用户的邮件存放目录 BASH:记录当前bash shell的路径。 BASH_SUBSHELL:记录当前子shell的层次。BASH_SUBSHELL是从0开始计数的整数。 BASH_VERSINFO:是一个数组包含六个元素,这六个元素显示bash的版本信息。 BASH_VERSION:显示shell版本的信息。 DIRSTACK:记录了栈顶的目录值,初值为空。 GLOBLGNORE:是由冒号分割的模式列表,表示通配时忽略的文件名集合。 GROUPS:记录当前用户所属的组。 HOME:记录当前用户的家目录,由/etc/passwd的倒数第二个域决定。 HOSTNAME:记录主机名。 HOSTTYPE和MACHTYPE:都是记录系统的硬件架构。 IFS:用于设置指定shell域分隔符,默认状况下为空格。 OSTYPE:记录操做系统类型。 PPID:是建立当前进程的进程号,即当前进程的父进程号 REPLY:REPLY变量与read和select有关。 SECONDS:记录脚本从开始到结束耗费的时间。 SHELL:显示当前所用的shell SHELLOPTS:记录了处于“开”状态的shell选项列表,它只是一个只读变量。 SHLVL:记录了bash嵌套的层次,通常来讲,咱们启动第一个Shell时。$SHLVL=1。若是在这个Shell中执行脚本,脚本中的$SHLVL=2。 TMOUT:用来设置脚本过时的时间,好比TMOUT=3,表示该脚本3秒后过时。 UID: 已登用户的ID USER:显示当前用户名字

用于查询与修改系统的本地化(locale)与键盘布局的设置:localectl

localectl list-locales      列出全部可用的 locale

设置系统的本地化环境变量(能够一次设置多个)
例如 "LANG=zh_CN.utf8", "LC_MESSAGES=en_US.utf8" 等等。

localectl set-locale LANG=zh_CN.UTF-8   --- centos7修改字符集信息

自定义环境变量

export PS1='\[\e[36;1m\][\u@\h \W]\$ \[\e[1;37m\]'

脚本示例

bc.sh

四则运算计算器

#!/bin/bash

# 1
if [[ $# == 2 ]];then
  echo $1+$2=$(($1+$2))
  echo $1-$2=$(($1-$2))
  echo $1*$2=$(($1*$2))
  echo $1/$2=$(($1/$2))
  exit
fi
# 2
if [[ $# == 0 ]];then
  read -p "请输入参数1: " var1
  read -p "请输入参数2: " var2
  if [[ $var1 == "" || $var2 == "" ]];then
# 3
    var1=10
    var2=10
  fi
  echo $var1+$var2=$(($var1+$var2))
  echo $var1-$var2=$(($var1-$var2))
  echo $var1*$var2=$(($var1*$var2))
  echo $var1/$var2=$(($var1/$var2))
  exit
fi

echo 请输入2个参数!
rabbit_chook.sh

鸡兔同笼脚本:

输入头数:35 输入脚个数:94

输出x只兔、y只鸡

算法:

x+y=35(头数) 2x+4y=94(脚数)

94/2=47-35=12(兔子个数)35-12=23(鸡个数)

let

 #!/bin/bash
 read -p "please input head num: " head
 read -p "please input floot num: " floot
 let x=$floot/2-$head
 let y=$head-$x
 echo "rabbit=$x;chook=$y"

expr

 #!/bin/bash
 read -p "please input head num: " head
 read -p "please input floot num: " floot
 x=`expr $floot / 2 - $head`
 y=`expr $head - $x`
 echo "rabbit=$x;chook=$y"

$[ ]

#!/bin/bash
read -p "please input head num: " head
read -p "please input floot num: " floot
rabbit=$[(floot-2*head)/2]
chook=$[head-rabbit]
echo "rabbit=$rabbit;chook=$chook"

$(())

#!/bin/bash
read -p "please input head num: " head
read -p "please input floot num: " floot
rabbit=$((floot/2-head))
chook=$((head-rabbit))
echo "rabbit=$rabbit;chook=$chook"
9x9.sh
#!/bin/bash
for a in {1..9};do
    for b in `seq 1 $a`;do
    let c=$a*$b ;echo -e "${a}x${b}=$c\t\c"
    done
    echo   
done
[root@oldboyedu-lnb ~]# sh 9x9.sh
1x1=1	
2x1=2	2x2=4	
3x1=3	3x2=6	3x3=9	
4x1=4	4x2=8	4x3=12	4x4=16	
5x1=5	5x2=10	5x3=15	5x4=20	5x5=25	
6x1=6	6x2=12	6x3=18	6x4=24	6x5=30	6x6=36	
7x1=7	7x2=14	7x3=21	7x4=28	7x5=35	7x6=42	7x7=49	
8x1=8	8x2=16	8x3=24	8x4=32	8x5=40	8x6=48	8x7=56	8x8=64	
9x1=9	9x2=18	9x3=27	9x4=36	9x5=45	9x6=54	9x7=63	9x8=72	9x9=81
chess_board.sh
#!/bin/bash
red="\033[1;41m  \033[0m"
yellow="\033[1;43m  \033[0m"
 
for i in {1..8};do
        if [ $[i%2] -eq 0 ];then
                for i in {1..4};do
                        echo -e -n "$red$yellow";
                done
                echo
        else
                for i in {1..4};do
                        echo -e -n "$yellow$red";
                done
                echo
        fi
done
color_isosceles_triangle.sh
#!/bin/bash
read -p "Please input a num: " num
if [[ $num =~ [^0-9] ]];then
        echo "input error"
else
        for i in `seq 1 $num` ;do
                xing=$[2*$i-1]
                for j in `seq 1 $[$num-$i]`;do
                        echo -ne " "
                done
                for k in `seq 1 $xing`;do
                        color=$[$[RANDOM%7]+31]
                        echo -ne "\033[1;${color};5m*\033[0m"
                done
                echo
        done
fi
[root@oldboyedu-lnb ~]# sh color_isosceles_triangle.sh
Please input a num: 5
    *
   ***
  *****
 *******
*********
systeminfo.sh

显示当前主机系统信息,包括主机名,IPv4地址,操做系统版本,内核版本,CPU型号,内存大小,硬盘大小

#! /usr/bin/sh
hn=`uname -nr | awk '{print $1}'`
ipv4=`ip route | grep -o 'src.*$' | awk '{print $2}'`
os=`cat /etc/redhat-release`
ke=`uname -nr | awk '{print $2}'`
cpu=`lscpu | grep 'Ven.*' | awk '{print $3}'`
me=`free -h | tail -1 | awk '{print $2}'`
sda=`fdisk -l | head -2 | awk '{print $3}' | tail -1`
echo "hostname:$hn"
echo "IPv4:$ipv4"
echo "OS version:$os"
echo "Kernel version:$ke"
echo "CPU model:$cpu"
echo "SWAP size:$me"
echo "Disk size:$sda G"
sysinfo.sh

带颜色显示当前主机系统信息,包括系统版本,内核版本,硬盘使用率,主机名

#!/usr/bin/env bash
# ------------------------------------------
# Filename: hello.sh
# Revision: 1.0
# Date: 2020/10/21
# Author: wu
# Email: wu@gmail.com
# Description: This is the first script
# Copyright (C): 2020 All rights reserved
# License: GPL
# ------------------------------------------
# RED is content color
# REDD is content color
RED="\033[1;31m"                                                     
REDD="\033[0m"
echo -e OS Version is $RED`cat /etc/centos-release`$REDD
echo -e Disk used is $RED`df | grep /dev/sd | tr -s " " |cut -d" " -f5 | sort -nr | head -n1`$REDD
echo -e Kernel is $RED`uname -r`$REDD
echo -e "Host name  is  $RED`hostname`"$REDD
create_sh.sh

编写生成脚本基本格式的脚本,包括做者,联系方式,版本,时间,描述等

#!/usr/bin/env bash
# ------------------------------------------
# Filename: hello.sh
# Revision: 1.0
# Date: 2020/10/21
# Author: wu
# Email: wu@gmail.com
# Description: create script and start
# Copyright (C): 2020 All rights reserved
# License: GPL
# ------------------------------------------
if [ $# -lt 1 ]; then
     echo '至少应该给一个参数!'
     exit 1
fi
cat > $1 <<EOF
#!/usr/bin/env bash
# ------------------------------------------
# Filename: $1
# Revision: 1.0
# Date: `date +%F`
# Author: wu
# Email: wu@gmail.com
# Description: This is the $1 script
# Copyright (C): 2020 All rights reserved
# License: GPL
# ------------------------------------------

EOF
chmod +x $1
vim + $1
backup.sh

每日将/etc/目录备份到/backup/etcYYYYmm-dd中

#! /usr/bin/sh
if [[ ! -d '/backup' ]]; then
    mkdir /backup
fi
cp -af /etc/ /backup/etc$(date -d 'today' +'%Y%m-%d')
echo "/etc/已备份"
chmod u+x backup.sh
echo '00 0 9 * * * root /root/backup.sh #天天早上九点执行backup.sh该文件' >> /etc/crontab
user_no_nologin.sh

统计出/etc/passwd文件中,默认shell为非/sbin/nologin的用户个数,并将用户都显示出来

#! /usr/bin/sh
getent passwd | grep -v /sbin/nologin
user_max_uid.sh

查出用户UID最大值的用户名、UID及shell类型

#! /usr/bin/sh
getent passwd | sort -t: -k3 -nr | head -n1
memory

显示占用系统内存最多的进程

#! /usr/bin/sh
ps aux | sort -k4 -nr | head -n1
disk.sh

显示当前硬盘分区中空间利用率最大的值

#! /usr/bin/sh
echo `df|egrep -o '[0-9]{1,3}%'|sort -nr|head -1`
links.sh

显示正链接本主机的每一个远程主机的IPv4地址和链接数,并按链接数从大到小排序

#! /usr/bin/sh
echo `netstat -nt | awk '{print $4}' | egrep '[0-9.]' | cut -d: -f1 | sort | uniq -c | sort -nr`
sumid.sh

计算/etc/passwd文件中的第10个用户和第20用户的UID之和

#! /usr/bin/sh
echo `cut -d: -f3 /etc/passwd | sed -n '10p;20p' | awk '{sum += $1};END {print sum}'`
sumspace.sh

传递两个文件路径做为参数给脚本,计算这两个文件中全部空白行之和

#! /usr/bin/sh
echo `cat $1 $2 | sed -n '/^$/p' | wc -l`
sumfile.sh

统计/etc, /var, /usr 目录中共有多少个一级子目录和文件

#! /usr/bin/sh
# c is Number of parameters
c=3
echo "`ll /etc /var /usr | wc -l` -($c*2+($c-1))" | bc
argsnum.sh

接受一个文件路径做为参数;若是参数个数小于1,则提示用户“至少应该给一个参数”,并当即退出;若是参数个数不小于1,则显示第一个参数所指向的文件中的空白行数

#! /usr/bin/sh
if [ $# -lt 1 ]; then
     echo '至少应该给一个参数!'
     exit 1
fi
cat $1 | sed -n '/^$/p' | wc -l
hostping.sh

接受一个主机的IPv4地址作为参数,测试是否可连通。若是能ping通,则提示用户“该IP地址可访问”;若是不可ping通,则提示用户“该IP地址不可访问”

#! /usr/bin/sh
if [ $# -lt 1 ]; then
     echo '至少应该给一个参数!'
     exit 1
fi
if ping -c1 -W2 $1; then
     echo '该IP地址可访问'
else
     echo '该IP地址不可访问'
fi
#! /usr/bin/sh
ping -c1 -W2 $1
[ $? -eq 0 ] && echo '该IP地址可访问' || echo '该IP地址不可访问'
hostsping.sh

使用for和while分别实现:

测试192.168.0.0/24网段内,地址是否可以ping通,若ping通则输出"success!",若ping不通则输出"fail!"

for循环实现

#!/bin/bash
NUM=`seq 1 254`

for IP in ${NUM};do
   HOST_IP="192.168.0.${IP}"
   ping -c 2 -w 3 ${HOST_IP} &> /dev/null
   if [ $? -eq 0 ];then
      echo "success!" && echo ${HOST_IP} >> /tmp/ip_success.txt
   else
      echo "fail!"  && echo ${HOST_IP} >> /tmp/ip_fail.txt
   fi
done

while循环实现

#!/bin/bash
IP=192.168.0
NUM=1

while [ $NUM -lt 255 ];do
  ping -c2 -W2 $IP.$NUM &> /dev/null
  if [ $? -eq 0 ];then
     echo "$IP.$NUM success!"
  else
     echo "$IP.$NUM fail!"
  fi
  let NUM++
done
checkdisk.sh

工做日,每10分钟执行一次,检查磁盘分区空间和inode使用率,若是超过80%,就发广播警告空间将满

#! /usr/bin/sh
sd=`df | egrep -o '[0-9]{1,3}%' | sort -nr | head -1 | egrep -o '[0-9]{1,3}'`
inode=`df -ih | egrep -o '[0-9]{1,3}%' | sort -nr | head -1 | egrep -o '[0-9]{1,3}'`
if [ $sd -gt 80 -o $inode -gt 80 ]; then
     wall 'Disk space will be full!'
else
     echo '磁盘分区空间和inode使用率正常'
fi
echo "*/10 * * * 1-5 /usr/bin/bash /server/script/checkdisk.sh" >> /var/spool/cron/root

每10分钟执行一次,检查磁盘分区空间、CPU和内存使用率,若是超过80%,就发广播警告空间将满

#! /usr/bin/sh
sd=`df | egrep -o '[0-9]{1,3}%' | sort -nr | head -1`
cpu=`cat /proc/stat|grep '^cpu[0-9]'|awk '{used+=$2+$3+$4;tolused+=$2+$3+$4+$5+$6+$7+$8} END{printf ("%.0f",used/tolused*100)}'`
mem=`free -t | tail -1 | awk '{printf ("%.0f",$3/$2*100)}'`

if [ ${sd%\%} -gt 80 ]; then
    wall 'Disk space will be full!'
elif [ $inode -gt 80 ]; then
    wall 'CPU space will be full!'
elif [ $mem -gt 80 ]; then
    wall 'Mem space will be full!'
fi
echo "*/10 * * * * /usr/bin/bash /server/script/checkdisk.sh" >> /var/spool/cron/root
checkip.sh

编写脚本/server/script/checkip.sh,每5分钟检查一次,若是发现经过ssh登陆失败次数超过10次,自动将此远程IP放入Tcp Wrapper的黑名单中予以禁止防问

写法一:

#!/bin/bash

while true;do
  awk '/Failed/{ip[$(NF-3)]++}
  END {
        for(i in ip)
        {
          if(ip[i]>=2)
          {
            system("echo sshd:"i" >> /etc/hosts.deny")
          }
        }
      }' /var/log/ssh.log
  sleep 5m
done

写法二:

#!/bin/bash

while true;do
# 收集IP出现失败的次数
  ipnum=`awk '/Failed/{print $(NF-3)}'  /var/log/ssh.log  |uniq -c |awk '{print $1}'` 
# 统计IP地址
  ip=`awk '/Failed/{print $(NF-3)}'  /var/log/ssh.log  |uniq -c |awk '{print $2}'`
# 当IP地址出现次数大于等于3,将IP地址拉入黑名单。
  if [ $ipnum -ge 3 ] ;then
     echo "sshd:$ip" >> /etc/hosts.deny
  fi
  sleep 5m
done

执行方式:sleep 5分钟或者加入计划任务,每5分钟执行一次

$ crontab -l
*/5 * * * *  /server/script/checkip.sh
checkDOS.sh

解决DOS攻击生产案例: 根据web日志或者或者网络链接数,监控当某个IP并发链接数或者短时内PV达到100,调用防火墙命令封掉对应的IP,监控频率每隔5分钟。防火墙命令为: iptables -A INPUT -s IP -j REJECT

#!/bin/bash
# environment variable 
source /etc/profile 
iplist=$(ss -tan | awk -F "[[:space:]]+|:" '/ESTAB/{ip[$(NF-2)]++}END{
   for(i in ip)
   {print i,ip[i]}
  }' | awk '{if($2>100)print $1}')
for ip in $iplist
do
  iptables -I INPUT -s $ip -j DROP
  echo "$ip is drop!"
done

加入计划任务,每5分钟执行一次

$ crontab -l
*/5 * * * *  /server/script/checkDOS.sh
per.sh

判断当前用户对指定参数文件,是否不可读而且不可写

#! /usr/bin/sh
if [ -r $1 -a -w $1 ]; then
    echo "$1并不是不可读而且不可写"
else
    echo "$1不可读而且不可写"
fi
excute.sh

判断参数文件是否为sh后缀的普通文件,若是是,添加全部人可执行权限,不然提示用户非脚本文件

#! /usr/bin/sh
if [[ -f $1 && `echo "$1" | cut -d. -f2` = sh ]]; then
    chmod a+x $1
else
    echo "$1非脚本文件"
fi
nologin.sh

禁止普通用户登陆系统

#! /usr/bin/sh
echo "System maintenance, no login." > /etc/nologin
login.sh

容许普通用户登陆系统

#! /usr/bin/sh
\mv /etc/nologin /root/nologin
createuser.sh

使用一个用户名作为参数,若是指定参数的用户存在,就显示其存在,不然添加之;显示添加的用户的id号等信息

#! /usr/bin/sh
if id $1 &> /dev/null; then
    id $1
else
    useradd $1
    id $1
fi

create_user_home.sh

接受二个位置参数,magedu和/www,判断系统是否有magedu,若是没有则自动建立magedu用户,并自动设置家目录为/www

#!/bin/bash
id $1 &> /dev/null
if [ $? -eq 0 ]; then
    id $1
else
    useradd -d $2 $1
    id $1
fi
yesorno.sh

提示用户输入yes或no,并判断用户输入的是yes仍是no,或是其它信息

#! /usr/bin/sh
read -p "Please input:yes/no: " name
if [ $name = yes ]; then
    echo 'You input: yes'
elif [ $name = no ]; then
    echo 'You input: no'
else
    echo 'You input: other info'
fi
filetype.sh

判断用户输入文件路径,显示其文件类型(普通,目录,连接,其它文件类型)

#! /usr/bin/sh
if [ $# -lt 1 ]; then
    echo '至少应该给一个参数!'
    exit 1
elif [ -L $1 ]; then
    echo '$1 is a Symbolic link'
elif [ -d $1 ]; then
    echo '$1 is a directory'
elif [ -f $1 ]; then
    echo '$1 is a normal file'
else
    echo '$1 is a other file type'
fi
checkint.sh

判断用户输入的参数是否为正整数

#! /usr/bin/sh
if [ $# -lt 1 ]; then
    echo '至少应该给一个参数!'
    exit 1
fi
if expr $1 + 1 &> /dev/null; then
    if [ $1 -ge 0 ]; then
    	echo "$1是正整数"
    fi
else
    echo "$1不是正整数"
fi
#! /bin/bash
if [ $# -gt 1  ];then
  echo "只能判断一个参数!"
  exit
fi
if [[ -z $1 ]];then
  echo "输入为空"
  exit
fi
if [[ $1 =~ ^[0-9-]?[0-9]+$ ]];then
  echo "输入为整数"
  exit
fi
echo "输入不是整数"
pathadd.sh

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

#! /usr/bin/sh
echo "export PATH=$PATH:/usr/local/apache/bin" >> /etc/profile
source /etc/profile
rootlogin.sh

用户 root 登陆时,将命令指示符变成红色,并自动启用以下别名: rm='rm –i' cdnet='cd /etc/sysconfig/network-scripts/' editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0' editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno16777736 或 ifcfg-ens33' (若是系统是CentOS7)

#! /usr/bin/sh
cat <<EOF>> /etc/profile
export PS1="\e[31m[\u@\h \W]\$ \e[0m"
alias rm='rm –i'
alias cdnet='cd /etc/sysconfig/network-scripts/'
alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'
EOF
loginissue.sh

任意用户登陆系统时,显示红色字体的警示提醒信息“Hi,dangerous!”

#! /usr/bin/sh
echo 'echo -e "\e[1;31mHi,dangerous!\e[0m"' >> /etc/profile
reset.sh

用户的环境初始化:包括别名,登陆提示符,vim的设置,环境变量等

#! /usr/bin/sh
selinux.sh

开启或禁用SELinux

#!/bin/bash

if [[ $1 =~ ^[Oo][Nn]$ ]]; then
  sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config &>/dev/null
  setenforce 1 &>  /dev/null 
elif [[ $1 =~ ^[Oo][Ff][Ff]$ ]]; then
  sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config &>/dev/null 
  setenforce 0 &>/dev/null
else
  echo "you are error,please input on or off !!!"
fi
checkganglia.sh

当客户机数量不为10的时候,进行报错;当客户机ganglia服务没有启动时,进行报错,而且筛选出全部没有启动ganglia的客户机

#!/bin/bash
DATE=`date +%Y%m%d`
filename="ganglia-${DATE}.log"
prefix="ganglia-${DATE}"
hosts=`grep test@ /tmp/log/ganglia/${filename} | wc -l`
pids=`grep gmond.pid /tmp/log/ganglia/${filename} | wc -l`
if [ ${hosts} != 10 ]
then
    echo "Some hosts are offline!" >> /tmp/log/ganglia/error-${DATE}.log
fi

if [ ${hosts} != ${pids} ]
then
    echo "Some ganglia services have stopped!" >> /tmp/log/ganglia/error-${DATE}.log
    cd /tmp/log/ganglia/
    csplit /tmp/log/ganglia/${filename} /test@/ -n2 -s {*} -f ${prefix} -b ".log.%02d"
    rm ${prefix}.log.00
    for file in /tmp/log/ganglia/${prefix}.log.*
    do 
    if [ -f "${file}" ]
    then
        #echo "${file} is file"
        if [ `grep gmond.pid ${file} | wc -l` == 0 ]
        then
            echo `grep test@ ${file}` >> /tmp/log/ganglia/error-${DATE}.log
        fi
    fi
    done
fi
[root@linuxcool ~]# crontab -l 
# 天天查看一下ganglia的状态,并保存到/tmp/log/ganglia目录
0 0 * * * /usr/bin/parallel-ssh -h /home/test/hosts.txt -t 30 -i 'ps aux | grep gmond' > /tmp/log/ganglia/ganglia-`date +\%Y\%m\%d`.log
# 自动筛选报错
10 0 * * * /bin/bash /home/test/checkganglia.sh

返回页首

Linux基础

CentOS7

tr命令

tr [OPTION]... SET1 [SET2] 转换和删除字符
   -c –C --complement      取字符集的补集
   -d --delete             删除SET1的字符
   -s --squeeze-repeats    把连续重复的字符以单独一个字符表示
   -t --truncate-set1      将SET1转化为SET2
tr -d a < filename         删除文件filename标准输出中的a
tr -s '\n' < filename      删除文件filename标准输出中的空行
cat filename |tr a-z A-Z       将文件filename中标准输出的小写字母所有转换成大写字母
or
cat filename |tr [:lower:] [:upper:] 	

tr -cd '0-9a-zA-Z' < /dev/urandom | head -c8  # 生成8位随机数

收集文本统计数据wc

计数单词总数、行总数、字节总数和字符总数
能够对文件或STDIN中的数据运行
wc story.txt
39 237 1901 story.txt
行数 字数 字节数
经常使用选项
-l 只计数行数
-w 只计数单词总数
-c 只计数字节总数
-m 只计数字符总数
-L 显示文件中最长行的长度

uniq

uniq命令:从输入中删除先后相接的重复的行
uniq [OPTION]... [FILE]...
-c: 显示每行重复出现的次数
-d: 仅显示重复过的行
-u: 仅显示未曾重复的行
注:连续且彻底相同方为重复
常和sort 命令一块儿配合使用:
sort userlist.txt | uniq -c

文本排序sort

把整理过的文本显示在STDOUT,不改变原始文件
sort [options] file(s)
经常使用选项
-r 执行反方向(由上至下)整理
-R 随机排序
-n 执行按数字大小整理
-f 选项忽略(fold)字符串中的字符大小写
-u 选项(独特,unique)删除输出中的重复行
-t c 选项使用c作为字段界定符
-k X 选项按照使用c字符分隔的X列来整理可以使用屡次

bash如何展开命令行  

  • 把命令行分红单个命令词

  • 展开别名

  • 展开大括号的声明({})

  • 展开波浪符声明(~)

  • 命令替换$()和``)

  • 再次把命令行分红命令词

  • 展开文件通配(*、?、[abc]等等)

  • 准备I/0重导向(<、>)

  • 运行命令

防止扩展 

反斜线\会使随后的字符按原意解释

$echoYourcost:\$5.00
Yourcost:$5.00

加引号来防止扩展 •单引号'防止全部扩展 •双引号"也防止全部扩展,可是如下状况例外:

$(美圆符号)-变量扩展
(反引号)-命令替换
\(反斜线)-禁止单个字符扩展,转义单个字符
!(叹号)-历史命令替换

1.简单的介绍shell 1) 为何要使用shell shell的做用 a. 安装操做系统 手工方式安装

   自动化安装操做系统 
	     kickstart  底层shell脚本
		   cobbler    底层shell脚本

b. 初始化操做系统

	   SSH优化      关闭SElinux 防火墙放行须要的端口(80 443 22修改 10050) YUM源 时间同步 系统最大描述符 
	   内核参数优化 字符集优化 禁止开机自动启动 修改主机名称 (修改公司网卡名称)...
	   手动  命令行安全
	   写入shell脚本(经常使用)
  c. 安装服务 Nginx PHP MySQL Rsync等等...  针对不一样的版本写入shell脚本自动安装
	d. 配置服务
	e. 启动服务 全部的服务底层的启动方式都是使用的shell脚本

公司本身研发的程序

 nohup python3.5 test.py --redis --port --mysql --port -a xxxx &

复制一下 写入脚本 sh start_test_py.sh 如何中止py程序

  ps axu|grep test.py |grep -v grep|awk '{print $2}'|xargs kill -9

复制一下 写入脚本 sh stop_test_py.sh 把py的进程的端口和PID取出来 来判断是否运行

	f. 日志统计 查看程序运行的状况 统计咱们须要的数据
	   日志切割 定时任务+脚本 
	   统计数据 定时任务+脚本  ---> 经过邮件发送给管理员
	   ELK 日志统计界面  py开发日志界面 py界面----> 数据库 <----数据   日志展现
g. 监控 监控服务 服务端口是否存在 服务是否存在 服务器的硬件资源使用状况 状态 日志 网络
   Zabbix	经过脚本统计---> 测试---> 添加到zabbix服务 (cacti监控流量 Nagios宽带运营商 IT公司)  
减小重复性的工做

2) 学习shell编程所用到的知识 必须熟练掌握

   a. vim编辑器 课下快捷键
   b. xshell crt
   c. shell基础命令60个左右  回顾基础命令
   d. 三剑客命令 grep sed awk

3) 如何学习shell编程

   a. 能读懂shell脚本---> 模仿---> 本身修改 ---> 本身写 ---> 熟练运用(基础命令) 重复性
   b. 有编程能力(写的越多 编程能力越高)
   c. 找一本适合本身的教材 或者 完善的文档 (脚本功能框架)
   e. 多练多操做
   f. 切记拿来直接使用,语句理解后在使用
  1. shell初步入门 1)什么是shell

      shell是命令解释器 负责翻译咱们输入的命令 输入的ls pwd命令都是shell解释器给咱们运行的
     交互式   咱们输入命令,shell负责解释执行咱们输入的命令,而且把结果输出到屏幕的过程称为交互式
    		  用户签退exit 关闭xshell shell终止
     非交互式 不和咱们交互,读取存在到文本中的shell命令,读取到文件结尾,shell结束

    2)什么是shell脚本

      把可执行的命令统一放入到一个文本中,称为shell脚本 包含了 判断语句 循环语句 数组等

    3)脚本语言的种类

      编译型 c c++
     解释型 python ruby
     脚本型 python shell javascript
     其余语言: PHP html go
     问: 编译型语法是最快的吗 看优化
     面试题: Linux中默认的shell解释器是 bash
     shell和py的区别
     shell处理底层的能力较强 全部的服务都是shell编写 一键优化 一键安装 一键统计(awk)
     py主要做用能够写界面 自动化管理平台CMDB 功能需求

    4)书写脚本的规范

     a. 脚本存放固定的目录 统一管理 /server/scripts
     b. 脚本使用.sh结尾    让咱们能识别是shell脚本
     c. 脚本命名 见名知其意 start_nginx.sh stop_nginx.sh
     d. 脚本内的开头使用解释器  #!/bin/bash 
     e. 脚本内的注释最好不用中文(能够用)
     f. 脚本内的成对的符号一次性书写完毕 语法书写完在写内容

    5)写第一个脚本 使用vim编辑test.sh 在屏幕上输出 Hellow World!

     [root@shell ~]# cat test.sh
     #!/bin/sh
     echo "Hello World!"

    执行脚本经常使用的三种方式: 父shell和子shell的区别 子shell能够继承父shell的变量 /etc/profile 变量 全部的子shell均可使用 使用bash或sh执行shell脚本 都是在子shell中运行的

    1. 使用sh或者bash方式运行 开启了一个子shell运行里面的内容

    [root@shell ~]# sh test.sh
      Hello World!
    1. 使用全路径方式执行脚本

    [root@shell ~]# /root/test.sh
    -bash: /root/test.sh: Permission denied
    [root@shell ~]# ./test.sh
    -bash: ./test.sh: Permission denied
     增长执行权限chmod
     [root@shell ~]# chmod +x test.sh 
     [root@shell ~]# ll
      total 4
      -rwxr-xr-x 1 root root 123 Oct 15 10:49 test.sh
      [root@shell ~]# /root/test.sh 
      Hello World!
      /bin/bash
      [root@shell ~]# ./test.sh
      Hello World!
      /bin/bash
    1. 使用. 或者source 在父进程中执行shell脚本

    [root@shell ~]# . test.sh 
    Hello World!
    /bin/bash
    [root@lb02 ~]# name=oldboy
    [root@shell ~]# echo $name
    oldboy

    不经常使用的执行方式

     [root@shell ~]# echo pwd|bash
     /root
     [root@shell ~]# sh < test.sh
     Hello World!
     /bin/bash
  2. shell经常使用的基础变量 1) 什么是变量

    系统中的变量: $PATH $LANG $BASH $PS1
      x=1 y=x+1 y=2
      x和y都是变量的名称 等号后面的是变量的值
    name=oldboy
    使用一个固定的值表明不固定的值(数字 字符串 命令)

    2) 环境变量分类

    环境变量 (全局变量)  国法  全部的shell都生效
    普通变量 (局部变量)  家规  只针对当前的shell生效  自定义的变量
    查看系统环境变量 env

    3) 按照生命周期划分

      临时生效 只是在当前shell中生效 关闭则失效 使用export直接定义便可
       永久生效 对当前系统全部shell生效 写入/etc/profile
       不加export 只对当前的shell生效
       加export   对当前的窗口的父shell和子shell生效

    4) 环境变量配置文件执行的顺序

    /etc/profile
    读取家目录下全部的环境变量文件
    .bash_profile 
    .bashrc
    /etc/bashrc

5) 定义环境变量 环境变量的名称定义: 字母数字下划线开头的组合 见名知其意 等号两端不能有空格 不容许数字开头

  oldboy_age=18    # 全小写
  OLDBOY_AGE=25	   # 全大写 系统环境变量都大写
  oldboy_Age=30    # 小驼峰语法
  Oldboy_Age=35    # 大驼峰语法
  变量值的定义:  数字 字符串 和命令

三种值: 字符串定义 变量值中间不容许有空格 若是有须要加引号

  [root@shell ~]# test='I am lizhenya'	
  [root@shell ~]# oldboy_age=45
  [root@shell ~]# test=`pwd`		# 定义命令
  [root@shell ~]# echo $test		
  /root
  [root@shell ~]# test=$(ls)		# 定义命令
  [root@shell ~]# echo $test
  [root@shell ~]# ls
  test.sh

PS: 注意赋值反引号仍是双引号

  [root@shell ~]# date
	Thu Oct 15 11:46:55 CST 2020
	[root@shell ~]# time=`date`
	[root@shell ~]# echo $time
	Thu Oct 15 11:47:00 CST 2020
	[root@shell ~]# echo $time
	Thu Oct 15 11:47:00 CST 2020
  [root@shell ~]# time="date +%F+%M+%S"

6) shell脚本中特殊的位置变量

$0    # 表示脚本的名称 若是全路径执行则带全路径 可使用basename只获取名字
  $0
   [root@shell ~]# cat test.sh
	#!/bin/sh
	#print   hello world
	#Author  oldboy
	#date    20202020
	#version v1.0
	echo "Hello World!"
	echo $0
	[root@shell ~]# sh test.sh 
	Hello World!
test.sh
-------------------
$n 		# 表明了脚本的第n的参数 n为数字 若是是0呢 则为脚本名称 从1开始 从$9之后须要加{} 表示总体
-gt 是否大于
-ge 是否大于等于
-eq 是否等于
-ne 是否不等于
-lt 是否小于
-le 是否小于等于

	$#      # 表示脚本传参的个数
	使用方法 判断传参的个数
	[root@shell ~]# cat test.sh 
	[ $# -ne 2 ] && echo "请输入两个参数" && exit
	expr $1 + $2
	[ $# -ne 2 ] && echo "请输入两个参数" && exit
	echo $1
	echo $2

$? # 判断上一条命令的结果是否正确 0为正确 非0失败

[root@shell ~]# cat ping.sh 
ping -c2 -W1  www.baiduaaaaa.com &>/dev/null
[ $? -ne 0 ] && echo "ping不通" || echo "通了"
$$      # 获取当前脚本的PID
	[root@shell ~]# cat test.sh 
	#!/bin/sh
	#print   hello world
	#Author  oldboy
	#date    20202020
	#version v1.0
	echo $$ > /tmp/nging.pid
	sleep 300

$$      # 获取当前脚本的PID
$#      # 表示脚本传参的个数
$n 		  # 表明了脚本的第n的参数 n为数字 若是是0呢 则为脚本名称 从1开始 从$9之后须要加{} 表示总体
$0      #脚本名称
$?      # 判断上一条命令的结果是否正确 0为正确 非0失败
$!      # 获取上一个在后台运行脚本的PID号 排错使用
$_		  # 获取当前命令行的最后一个参数 相似于esc .
$*		  # 获取脚本传参的全部参数 在循环体中 不加双引号和$@相同 加双引号 把全部的参数当作一个参数	
$@      # 获取脚本传参的素有参数 在循环体中 不加双引号和$*相同 加双引号 把全部的参数当作独立的
小结: 经常使用的位置参数
$0  $# $n $?
  1. 脚本传参的三种方式 1) 直接传参

     [root@shell ~]# cat test.sh 
    echo name=$1
    echo age=$2
    [root@shell ~]# sh test.sh  oldboy 18
    name=oldboy
    age=18

    2) 赋值传参

    [root@shell ~]# cat test.sh
    #!/bin/sh
    name=$1
    age=$2
    echo -e "$name\n$age"
    [root@shell ~]# sh test.sh oldboy 20
    oldboy
    20

    3) read 传参 read 变量名称 进行赋值 read -p "用户提示" 变量名称

[root@shell ~]# cat read.sh 
#!/bin/bash
read -p "请输入你的姓名: " name
echo "你输入的姓名是 $name"
[root@shell ~]# cat read.sh 
#!/bin/bash
read -p "请输入你的姓名和年龄: " name age
echo "你输入的姓名是: $name 年龄是: $age"
[root@shell ~]# sh read.sh
请输入你的姓名和年龄: oldboy 20
你输入的姓名是: oldboy 年龄是: 20
[root@shell ~]# cat read.sh
#!/bin/bash
read -p "请输入你的姓名: " name 
read -p "请输入你的年龄: " age
echo "你输入的姓名是: $name 年龄是: $age"
[root@shell ~]# sh read.sh
请输入你的姓名: oldboy
请输入你的年龄: 20
你输入的姓名是: oldboy 年龄是: 20
案例: 使用read传参的方式 修改主机名称为shell 而且修改eth0网卡IP地址为88
[root@shell ~]# cat hostname.sh 
#!/bin/bash
eth0_cfg='/etc/sysconfig/network-scripts/ifcfg-eth0'
old_ip=`ifconfig eth0|awk 'NR==2{print $2}'|awk -F. '{print $NF}'`
read -p "please input hostname: " name
read -p "please input New IP: " IP
hostnamectl set-hostname $name
sed -i "s#$old_ip#$IP#g" $eth0_cfg
grep $IP $eth0_cfg
[root@shell ~]# cat ping.sh 
read -p "Please Input URL: " url
ping -c2 -W1  $url &>/dev/null
[ $? -ne 0 ] && echo "ping不通" || echo "通了"
重复赋值
[root@shell ~]# cat dir.sh
#!/bin/sh
dir1=/etc
dir2=/tmp
read -p "请输入须要备份的目录: " dir2
echo $dir1
echo $dir2
[root@shell ~]# sh dir.sh
请输入须要备份的目录: /opt
/etc
/opt
  1. 变量的子串及删除替换

    [root@shell ~]# test='I am oldboy'
    [root@shell ~]# echo $test
    I am oldboy
    [root@shell ~]# echo $test|awk '{print $2}'
    am
    [root@shell ~]# echo $test|cut -c3-4
    am

变量切片

nginx
[root@shell ~]# echo ${test:2:2}
am
[root@shell ~]# echo ${#test}
11
统计字符的长度
nginx
	[root@shell ~]# echo $test|wc -L
	11
	[root@shell ~]# expr length "$test"
	11
	[root@shell ~]# echo $test|awk '{print length}'
	11
统计出字符串小于3的单词 笔试题
I am lzhenya teacher I am 18
[root@shell ~]# cat for.sh
for i in I am lzhenya teacher I am 18
do
		[ ${#i} -lt 3 ] && echo $i
done	
	[root@shell ~]# sh for.sh
	I
	am
	I
	am
	18
[root@shell ~]# echo I am lzhenya teacher I am 18|xargs -n1|awk '{if(length<3)print}'
	I
	am
	I
	am
	18
	[root@shell ~]# echo I am lzhenya teacher I am 18|awk '{for(i=1;i<=NF;i++)if(length($i)<3)print $i}'
  I
	am
	I
	am
	18
子串的删除替换
[root@shell ~]# echo ${url}
www.baidu.com	
[root@shell ~]# echo ${url}|awk -F "w." '{print $3 }'
baidu.com
[root@shell ~]# echo ${url}
www.baidu.com
[root@shell ~]# echo ${url#www.}
baidu.com
[root@shell ~]# echo ${url#*.}
baidu.com
[root@shell ~]# echo ${url#*.*.}
com
贪婪匹配
[root@shell ~]# echo ${url##*.}
com
[root@shell ~]# echo ${url%.com}
www.baidu
[root@shell ~]# echo ${url%.*}
www.baidu
[root@shell ~]# echo ${url%.*.*}
www
[root@shell ~]# echo ${url%%.*}
www
[root@shell ~]# test=%66
[root@shell ~]# echo $test
%66
[root@shell ~]# echo ${test#%}
66
[root@shell ~]# echo ${test##}
#66
[root@shell ~]# echo ${test#\#}
66
变量替换
nginx
[root@shell ~]# echo $url
www.baidu.com
[root@shell ~]# echo $url|sed 's#www#WWW#g'
WWW.baidu.com
[root@shell ~]# echo $url
www.baidu.com
[root@shell ~]# echo ${url/w/W}
Www.baidu.com
[root@shell ~]# echo ${url//w/W}
WWW.baidu.com	
[root@shell ~]# echo ${url/baidu/sina}
www.sina.com
知识点回顾:
$0 $n $# $? $$ $! $* $@ $_
1.shell数值运算
  1) expr 只能作整数运算 不支持小数运算
  命令格式:
		   expr 整数1 空格 运算符 空格 整数2
[root@shell ~]# expr 1 +1
expr: syntax error
[root@shell ~]# expr 10 - 5
5
[root@shell ~]# expr 10 * 5
expr: syntax error
[root@shell ~]# expr 10 \* 5
50
[root@shell ~]# expr 10 / 5
2
[root@shell ~]# expr length oldboy	# 统计字符串长度
6

2) $[] 只支持整数运算 不支持小数运算

[root@shell ~]# echo $[1+1]
2
[root@shell ~]# echo $[1+1.5]
-bash: 1+1.5: syntax error: invalid arithmetic operator (error token is ".5")
[root@shell ~]# echo $[1+10]
11
[root@shell ~]# echo $[1-10]
-9
[root@shell ~]# echo $[100*10]
1000
[root@shell ~]# echo $[100/10]
10
[root@shell ~]# #100*10=1000
[root@shell ~]# echo 100*10=1000
100*10=1000
[root@shell ~]# echo 100*10=$[100*10]
100*10=1000
[root@shell ~]# num1=100
[root@shell ~]# num2=10
[root@shell ~]# echo $num1*$num2
100*10
[root@shell ~]# echo $num1*$num2=$[$num1*$num2]
100*10=1000

3) $(()) 只支持整数运算 不支持小数运算 PS: $()执行命令 $(()) 数值运算 执行效率最高

  [root@shell ~]# echo $((1+1))
	2
	[root@shell ~]# echo $((1+1.5))
	-bash: 1+1.5: syntax error: invalid arithmetic operator (error token is ".5")
	[root@shell ~]# echo $((100-100))
	0
	[root@shell ~]# echo $((100*100))
	10000
	[root@shell ~]# echo $((100/100))
	1

4) let 只支持整数运算 不支持小数运算 语法格式:

		let sum=1+1
输入结果: echo $sum
[root@shell ~]# let sum=1+1
[root@shell ~]# echo $sum
2
[root@shell ~]# let sum=100-100
[root@shell ~]# echo $sum
0
[root@shell ~]# let sum=100*100
[root@shell ~]# echo $sum
10000
[root@shell ~]# let sum=100/100
[root@shell ~]# echo $sum
1
[root@shell ~]# let sum=$num1+$num2
[root@shell ~]# echo $sum
110
[root@shell ~]# let i++
[root@shell ~]# echo $i
1
[root@shell ~]# let i++
[root@shell ~]# echo $i
2
[root@shell ~]# let i++
[root@shell ~]# echo $i
3
[root@shell ~]# echo i++ ===== i=i+1
i++ ===== i=i+1
[root@shell ~]# unset i
[root@shell ~]# let i=i+1
[root@shell ~]# echo $i
1
[root@shell ~]# let i=i+1
[root@shell ~]# echo $i
2
[root@shell ~]# let i++
[root@shell ~]# let i++
[root@shell ~]# let i++

5) bc 使用yum安装 yum -y install bc 支持整数和小数运算 语法格式: echo 10*10|bc

[root@shell ~]# echo 10*10|bc
100
[root@shell ~]# echo 10/10|bc
1
[root@shell ~]# echo 10-10|bc
0
[root@shell ~]# echo 10+10|bc
20
[root@shell ~]# echo 10+10.2|bc
20.2
[root@shell ~]# echo 10.5+10.2|bc
20.7
[root@shell ~]# echo 10.5*10.2|bc
107.1

其余运算 支持整数和小数运算 awk python awk语法: awk 'BEGIN{print 10*10}'

[root@shell ~]# awk 'BEGIN{print 10*10}'
100
[root@shell ~]# awk 'BEGIN{print 10-10}'
0
[root@shell ~]# awk 'BEGIN{print 10/10}'
1
[root@shell ~]# awk 'BEGIN{print 10+10}'
20
[root@shell ~]# awk 'BEGIN{print 10^10}'
10000000000
[root@shell ~]# echo 10 10|awk '{print $1*$2}' 
100
[root@shell ~]# echo 10 10|awk '{print $1^$2}' 
10000000000

案例: 作一个加减乘除的计算器 要求用户输入两个输入 显示加减乘除后的结果 使用三种传参方式 sh count.sh 10 10 输入的结果

  10+10=20
	  10-10=
	  10*10=
	  10/10=
[root@shell ~]# cat count.sh 
#!/bin/sh
[ $# -ne 2 ] && echo "输入两个参数多了不行" && exit
#1.直接传参
echo $1+$2=$[$1+$2]
echo $1-$2=$[$1-$2]
echo $1*$2=$[$1*$2]
echo $1/$2=$[$1/$2]
#2 赋值传参
num1=$1
num2=$2
echo $num1+$num2=$[$num1+$num2]
echo $num1-$num2=$[$num1-$num2]
echo $num1*$num2=$[$num1*$num2]
echo $num1/$num2=$[$num1/$num2]
#3 read传参
read -p "输入两个参数: " num1 num2
echo $num1+$num2=$[$num1+$num2]
echo $num1-$num2=$[$num1-$num2]
echo $num1*$num2=$[$num1*$num2]
echo $num1/$num2=$[$num1/$num2]
  1. 条件表达式之文件判断 语法格式: 方法1 test 表达式 文件|目录 例如: test -e /etc/passwd 方法2 [ -e file ] 经常使用表达式:

    [ -e file|dir ]   # 文件存在则为真 执行&& 不然执行||
    [ -f file ]		    # 判断文件是否存在而且为普通文件
    [ -d dir  ]       # 判断文件存在而且为目录
    [ -r file ]       # 文件存在而且可读
    [ -w file ]		    # 文件存在而且有写入权限
    [ -x file ]		    # 文件存在而且可执行
    [root@shell ~]# test -e /etc/passwd
    [root@shell ~]# echo $?
    0
    [root@shell ~]# test -e /etc/passwd;echo $?
    0
    [root@shell ~]# test -e /etc/passwdddddddd;echo $?
    1
    [root@shell ~]# test -e /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
    [root@shell ~]# test -f /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
    [root@shell ~]# test -d /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件不存在
    [root@shell ~]# test -x /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件不存在
    [root@shell ~]# test -r /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
    [root@shell ~]# test -w /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
[root@shell ~]# [ -e /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -f /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -d /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件不存在
[root@shell ~]# [ -d /etc/ ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -r /etc/ ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -w /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -x /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件不存在
[root@shell ~]# [ -x test.sh ] && echo "文件存在" || echo "文件不存在"
文件存在
判断中可使用命令的方式进行取值判断
[root@shell ~]# [ -f `ls test.sh` ]
使用and和or判断多个文件
[root@shell ~]# [ -f /etc/hosts -a -f /etc/passwd ]
[root@shell ~]# [ -f /etc/hosts -a -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -f /etc/hostsss -a -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
文件不存在
[root@shell ~]# [ -f /etc/hostsss -o -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -f /etc/hostsss -o -f /etc/passwddd ] && echo "文件存在" || echo "文件不存在"
文件不存在
案例1:
使用变量赋值一个目录,判断目录是否存在,若是存在则打包压缩,若是不存在则提示目录不存在
[root@shell ~]# dir=/etc/;[ -d $dir ] && tar zcvf test.tar.gz $dir || echo "文件不存在"
案例2:
判断一个目录是否存在 若是不存在则建立
[root@shell ~]# [ -d /hehe ] && mkdir hehe
[root@shell ~]# ll hehe
ls: cannot access hehe: No such file or directory
[root@shell ~]# ll /hehe
ls: cannot access /hehe: No such file or directory
[root@shell ~]# 
[root@shell ~]# [ -d hehe ] && mkdir hehe
[root@shell ~]# ll hehe
ls: cannot access hehe: No such file or directory
[root@shell ~]# [ -d hehe ] || mkdir hehe
[root@shell ~]# ll hehe
total 0
案例3:
判断环境变量文件函数库是否存在 存在则执行.
[root@shell ~]# cat test.sh 
#!/bin/sh
[ -f /etc/init.d/functions ] && . /etc/init.d/functions
ping -c2 $1 &>/dev/null
[ $? -eq 0 ] && action "ping $1 is ok" true || action "ping $1 is error" false
  1. 数值比较 整数 格式: test 整数1 表达式 整数2 [ 整数1 表达式 整数2 ]

    表达式:

    -eq   等于
    -ne   不等于
    -gt   大于
    -ge   大于等于
    -lt   小于
    -le   小于等于

    [] 判断支持正则 必须使用[[]] 在正则中使用如下符号

    =
    !=
    >=
    ><
    ><=
    [root@shell ~]# [ 10 -eq 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -ne 10 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ 10 -ne 100 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -gt 100 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ 10 -lt 100 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 100 -le 100 ] && echo "成立" || echo "不成立"
    成立

    使用变量的方式比较

    [root@shell ~]# echo $num1
    100
    [root@shell ~]# [ $num1 -le $num2 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# echo $num2
    10
    [root@shell ~]# [ $num1 -gt $num2 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ $num1 -ne $num2 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ $num1 -ne $num2 ] && echo "成立" || echo "不成立"
    [root@shell ~]# name='I am lizhenya'
    [root@shell ~]# echo $name
    I am lizhenya
    [root@shell ~]# for i in $name;do echo $i;done
    I
    am
    lizhenya
    [root@shell ~]# for i in $name;do echo ${#i};done
    1
    2
    8
    [root@shell ~]# for i in $name;do [ ${#i} -lt 3 ];done
    [root@shell ~]# for i in $name;do [ ${#i} -lt 3 ]&& echo $i;done
    I
    am
    [root@shell ~]# for i in $name;do [ ${#i} -gt 3 ]&& echo $i;done
    lizhenya

    [root@shell ~]# [ 10 != 10 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ 100 > 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 100 >= 10 ] && echo "成立" || echo "不成立"
    -bash: [: 100: unary operator expected
    不成立
    [root@shell ~]# [ 100 < 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [[ 100 < 10 ]] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [[ 100 > 10 ]] && echo "成立" || echo "不成立"
    成立

    案例1: 使用read或直接传参方式写一个数值比较脚本(使用数值表达式) 要求 输入两个数字 两个数字须要加判断 输入结果

       sh count.sh 100 10
    	   100>10
    
    [root@shell ~]# cat count.sh 
    #!/bin/sh
    [ $1 -gt $2 ] && echo "$1>$2"
    [ $1 -lt $2 ] && echo "$1<$2"
    [ $1 -eq $2 ] && echo "$1=$2"

    案例2: 统计磁盘使用率 若是超过3 则发送邮件通知管理员,正常则输出当前使用率 第一个步骤: 取出磁盘使用率的数值

    [root@shell ~]# df -h|awk 'NR==2{print $(NF-1)}'
    4%

    第二个步骤: 比较 优化取到的值

    [root@shell ~]#df -h|awk 'NR==2{print $(NF-1)}'|awk -F% '{print $1}'

    进行比较

    [root@shell ~]# [ `df -h|awk 'NR==2{print $(NF-1)}'|awk -F% '{print $1}'` -gt 3 ] && echo mail..... || echo ok
    mail.....

    第三个步骤: 写入脚本

    [root@shell ~]# cat disk.sh 
    #!/bin/bash
    use_disk=`df -h|awk 'NR==2{print $(NF-1)}'`
    [ ${use_disk%\%} -gt 80 ] && echo sendmail........ || echo "当前的磁盘使用率为 $use_disk"
    [root@shell ~]# sh disk.sh 
    当前的磁盘使用率为 4%

    案例3: 统计内存使用率 若是超过3 则发送通知管理员,不然输出当前的内存使用率 内存使用率的计算方式 使用的除以总共的乘以100 统计内存使用率:

    [root@shell ~]# free|awk 'NR==2{print $3/$2*100}'
    		5.46261
    数值比较 
        [ `free|awk 'NR==2{print $3/$2*100}'|awk -F. '{print $1}'` -gt 3 ] && echo sendmail.... || echo ok 
    写入脚本
    [root@shell ~]# cat use_mem.sh
    #!/bin/sh
    use_mem=`free|awk 'NR==2{print $3/$2*100}'`
    [ ${use_mem%.*} -gt 80 ] && echo sendmail...... || echo "当前内存使用率 ${use_mem}%"
    [root@shell ~]# sh use_mem.sh
    当前内存使用率 5.4916%
    [root@shell ~]# cat use_mem.sh
    #!/bin/sh
    . /etc/init.d/functions
    use_mem=`free|awk 'NR==2{print $3/$2*100}'`
    [ ${use_mem%.*} -lt 3 ] && action "sendmail......" false || action "当前内存使用率 ${use_mem}%" true

    案例4: 统计当前的负载 负载大于1 则发送通知 不然输出当前的负载使用状况

    yum -y install httpd
    systemctl start httpd
    ab -n800000 -c400 http://10.0.0.61/index.html
    [root@shell ~]# cat use_load.sh 
    #!/bin/sh
    use_load=`uptime|awk '{print $(NF-2)}'`
    [ ${use_load%.*} -gt 1 ] && echo sendmail............. || echo "当前系统的负载为 ${use_load%,}"

    案例5: 统计系统的主机名称 虚拟平台 系统版本 内核版本 eth0 IP地址 外网网卡IP地址 当前内存和磁盘的使用率 输出的结果: 当前系统主机名称为: shell 当前系统内存使用率: 20%

    [root@lb02 ~]# cat static.sh 
    #!/bin/bash
    eth0_cfg='/etc/sysconfig/network-scripts/ifcfg-eth0'
    old_ip=`ifconfig eth0|awk 'NR==2{print $2}'|awk -F. '{print $NF}'`
    read -p "please input hostname: " name
    read -p "please input New IP: " IP
    hostnamectl set-hostname $name
    sed -i "s#$old_ip#$IP#g" $eth0_cfg
    grep $IP $eth0_cfg
    
    use_virtualization=`hostnamectl|awk 'NR==6{print $NF}'`
    [ ${use_virtualization} ] && echo "当前系统的虚拟平台为  ${use_virtualization}"
    use_oprete=`hostnamectl|awk 'NR==7{print $(NF-1)}'`
    [ ${use_oprete} ] && echo "当前系统版本为 ${use_oprete}"
    use_kernal=`hostnamectl|awk 'NR==9{print $NF}'`
    [ ${use_kernal} ] && echo "当前系统内核版本为 ${use_kernal}"
    use_mem=`free|awk 'NR==2{print $3/$2*100}'`
    [ ${use_mem%.*} -gt 80 ] && echo sendmail...... || echo "当前内存使用率 ${use_mem}%"
    use_disk=`df -h|awk 'NR==2{print $(NF-1)}'`
    [ ${use_disk%\%} -gt 80 ] && echo sendmail........ || echo "当前的磁盘使用率为 $use_disk"
  2. 多整数比较

    -a and
    -o or
     [ 10 -eq 10 -a 100 -gt 50 -o 100 -ne 100 ]
     [root@shell ~]# [ 10 -eq 10 ]
    [root@shell ~]# [ 10 -eq 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# 
    [root@shell ~]# [ 10 -eq 10 -a 100 -gt 50 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -ne 10 -a 100 -gt 50 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# 
    [root@shell ~]# [ 10 -eq 10 -a 100 -gt 50 -o 100 -ne 100 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -eq 10 -o 100 -ne 100 ] && echo "成立" || echo "不成立"
    成立
    在正则中使用 && ||
     [root@shell ~]# [[ 10 -eq 10 && 100 -gt 20 ]] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [[ 10 -eq 10 || 100 -gt 20 ]] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [[ 10 -eq 11 || 100 -gt 20 ]] && echo "成立" || echo "不成立"
    成立
  3. 字符串比对

    [root@shell ~]# [ user = user ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ user = userrr ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ $USER = userrr ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ $USER = user ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# echo $USER
    root
    [root@shell ~]# [ $USER = root ] && echo "成立" || echo "不成立"
    成立
    -n //nozero 字符串长度不为0时为真
    -z //zero 字符串长度为0时为真
    [root@shell ~]# test=""
    [root@shell ~]# [ -z $test ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# test="aaa"
    [root@shell ~]# [ -z $test ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ -n $test ] && echo "成立" || echo "不成立"
    成立

    案例: 传参的时候不容许为空

    [root@shell ~]# cat test.sh
    #!/bin/sh
    [ -f /etc/init.d/functions ] && . /etc/init.d/functions
    [ -z $1 ] && echo "请输入url" && exit
    ping -c2 $1 &>/dev/null
    [ $? -eq 0 ] && action "ping $1 is ok" true || action "ping $1 is error" false
    [root@shell ~]# cat count.sh 
    #!/bin/sh
    [ $# -ne 2 ] && echo "请输入两个参数" && exit
    [ -z $1  ] && echo "请输入两个参数" && exit
    [ -z $2  ] && echo "请输入两个参数" && exit
    [ $1 -gt $2 ] && echo "$1>$2"
    [ $1 -lt $2 ] && echo "$1<$2"
    [ $1 -eq $2 ] && echo "$1=$2"
    read -p "请输入你的姓名: " name
    [ -z $name ] && echo "请输入姓名" && exit
    echo $name

    案例: 判断输入的是否为数字

    [root@shell ~]# expr 1 + 1.5
    expr: non-integer argument
    [root@shell ~]# echo $?
    2
    [root@shell ~]# expr 1 + qq
    expr: non-integer argument
    [root@shell ~]# echo $?
    2
    [root@shell ~]# expr 1 + 234
    235
    [root@shell ~]# echo $?
    0
    [root@shell ~]# cat count.sh 
    #!/bin/sh
    [ $# -ne 2 ] && echo "请输入两个参数" && exit
    expr $1 + $2 &>/dev/null
    [ $? -ne 0 ] && echo "请输入两个整数" && exit
    [ $1 -gt $2 ] && echo "$1>$2"
    [ $1 -lt $2 ] && echo "$1<$2"
    [ $1 -eq $2 ] && echo "$1=$2"
  4. 正则比对

  [root@shell ~]# [[ $user =~ ^r ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ ^r ]] && echo "成立" || echo "不成立"
  不成立
  [root@shell ~]# [[ user =~ user ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ r$ ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ r$ && oldboy =~ ^o ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ r$ && oldboy =~ ^a ]] && echo "成立" || echo "不成立"
  不成立
  [root@shell ~]# [[ user =~ r$ || oldboy =~ ^a ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ $num1 =~ ^[0-9]+$ ]] && echo "成立" || echo "不成立"
  不成立
  [root@shell ~]# cat count.sh 
  #!/bin/sh
  [ $# -ne 2 ] && echo "请输入两个参数" && exit
  [[ ! $1 =~ ^[0-9]+$ ]] && echo "请输入整数" && exit 
  [ $1 -gt $2 ] && echo "$1>$2"
  [ $1 -lt $2 ] && echo "$1<$2"
  [ $1 -eq $2 ] && echo "$1=$2"

练习 判断输入的是否为整数 判断输入的是否为空 expr -z [[ 正则判断 ]]

  1. if判断 语法格式:

    [ 10 -eq 10 ] && echo ok || echo error
    
    if [ 10 -eq 10 ];then
       echo ok
    fi

    单分支语法: 一个条件 一个结果 [ 你有钱 ] && 我就嫁给你

    if [ 你有钱 ];then 		
      我就嫁给你
    fi
    if [ 你有钱 ]
    then
        我就嫁给你
    fi
    双分支语法: 一个条件 两个结果
    if [ 你有钱 ];then			 [ 你有钱 ] && 我就嫁给你 || 拜拜
      我就嫁给你
    else
       拜拜
    fi
    多分支语法: 多个条件 多个结果
    if [ 你有钱 ];then
       我就嫁给你
    elif [ 你爸是李刚 ];then
       我也嫁给你
    elif [ 你活好 ];then
       我倒贴
    elif [ 你在老男孩学习 ];then
       先谈恋爱吧
    else
       拜拜
    fi

    案例: expr 判断输入的是否为整数

     [root@shell ~]# cat count.sh 
    	#!/bin/sh
    	expr $1 + $2 &>/dev/null
    	if [ $? -ne 0 ];then
    	   echo "请输入两个整数"
    	   exit
    	fi
    使用$# 判断传参的总个数
    	if [ $# -ne 2 ]
    	then
    	   echo "请输入两个参数"
    	   exit
    	fi
    使用正则匹配判断输入的是否为整数
    	if [[ ! $1$2 =~ ^[0-9]+$  ]];then
    	echo "请输入整数"
    	exit
    	fi
    使用-z判断是否为空
    read -p "please input hostname: " name
    if [ -z $name ];then
       echo "请输入姓名"
       exit
    fi

    案例: 不一样的操做系统版本安装不用的YUM源 获取操做系统版本号:

    [root@shell ~]# awk '{print $(NF-1)}' /etc/redhat-release 
    7.6.1810
    [root@oldboyedu-c6 ~]# awk '{print $(NF-1)}' /etc/redhat-release
    6.9
    [root@shell ~]# cat yum.sh
    #!/bin/sh
    #判断网络是否正常
    #ping的返回结果使用if判断
    os_version=`awk '{print $(NF-1)}' /etc/redhat-release`
    if [[ $os_version =~ ^7 ]];then
    判断wget是否存在
    which wget &>/dev/null
       if [ $? -ne 0 ];then
    		   yum -y install wget &>/dev/null
    			  if [ $? -eq 0 ];then
    			 echo "install wget is ok.............."
    			  else
    			echo "请检查你的网络或者源"
    			  fi
       fi
    	  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 https://mirrors.aliyun.com/repo/Centos-7.repo
    elif
       [[ $os_version =~ ^6 ]];then
       echo yum6..................
    else
       echo "请检查你的系统版本"
    fi

if案例: 根据需求安装不一样版本的PHP

菜单: 列出当前脚本的功能

	 [root@shell ~]# cat menu.sh 
	#!/bin/sh
	while true
	do
		echo -e "\t\t\t##################"
		echo -e "\t\t\t#1. INSTLL PHP7.1#"
		echo -e "\t\t\t#2. INSTLL PHP7.2#"
		echo -e "\t\t\t#3. INSTLL PHP7.3#"
		echo -e "\t\t\t#4. INSTLL PHP7.4#"
		echo -e "\t\t\t#5. INSTLL MySQL #"
		echo -e "\t\t\t#6. exit         #"
		echo -e "\t\t\t##################"
		read -p "请输入须要安装的PHP的序号[1|2|3]: " num
		if [ $num -eq 1 ];then
		   echo yum7.1....................
		elif [ $num -eq 2 ];then
		   echo yum7.2...................
		elif [ $num -eq 3 ];then
		   echo yum7.3...................
		elif [ $num -eq 4 ];then
		   echo yum7.4...................
	elif [ $num -eq 5 ];then
	   echo MySQL...................
	elif [ $num -eq 6 ];then
	   exit
	fi
done

案例: 使用菜单的方式和if判断 显示当前系统的 内存 CPU负载 登陆系统用户 当前的磁盘使用 退出 菜单: echo cat

 [root@shell ~]# cat os_menu.sh 
	#!/bin/sh
	menu(){
	cat<<EOF
							1 f.  查看系统内存	
							2 u.  查看CPU负载
							3 w.  查看当前用户
							4 d.  查看当前磁盘
							5 e.  exit
							6 h.  显示当前菜单
	EOF
	}
	menu
	while true
	do
		read -p "请输入要查看系统信息的编号或字母[1|f]: " num
		if [ $num = 1 -o $num = "f" ];then
		   free -h
		elif [ $num = 2 -o $num = u ];then 
		   uptime
		elif [ $num = 3 -o $num = w ];then
		   w
		elif [ $num = 4 -o $num = d ];then
		   df -h
		elif [ $num = 6 -o $num = h ];then
		   menu
		else
		   exit
		fi
	done

案例:猜数字游戏 系统随机生成一个数字, 让用户输入这个数字,若是大了 则提示大 小了则提示小了 相等则提示用户猜对了.

  1. 生成随机数

    [root@shell ~]# echo $((RANDOM%100+1))
    29
    [root@shell ~]# cat ran.sh 
    #!/bin/sh
    ran=`echo $((RANDOM%100+1))`
    while true
    do
    	let i++
    	read -p "请输入你要猜的数字1-100: " num
    	if [ $num -gt $ran ];then
    	   echo "你输入的大了"
    	elif [ $num -lt $ran ];then
    	   echo "你输入的小了"
    	else
    	   echo "恭喜你猜对了"
    	   echo "总共猜了 $i 次"
    		   exit
    	fi
    done   

case语句 语法格式:

case 变量 in
	  匹配变量1)
	     执行命令序列
		 ;;
      匹配变量2)
	     执行命令序列
		 ;;
	  匹配变量3)
	     执行命令序列
		 ;;
	  *)
	     执行命令序列
esac

案例: 查看系统信息

 [root@shell ~]# cat case.sh 
	#!/bin/bash
	menu(){
	cat<<EOF
					d. 查看磁盘
					f. 查看内存
					w. 查看用户
					u. 查看负载
					h. 显示菜单
					e. 退出脚本
	EOF
	}
	menu
	while true
	do
		read -p "请输入内容[d|f|w]: " test
		case $test in
			   d)
				 df -h
			   ;;
			   f)
				 free -h
			   ;;
			   w)
				 w
			   ;;
			   h)
				 clear
				 menu
			   ;;
			   *)
				echo "请输入菜单显示的系统项"
		esac
	done
 案例: shell跳板机
 [root@shell ~]# cat jumpserver.sh
 #!/bin/sh
 . /etc/init.d/functions
 WEB01=10.0.0.7
 WEB02=10.0.0.8
 LB01=172.16.1.5
 LB02=10.0.0.6
 MySQL=10.0.0.51
 NFS=10.0.0.31
 BACKUP=10.0.0.41
 menu1(){
 cat<<EOF
  1. 运维
  2. 开发
  3. 测试
  4. 菜单
 EOF
 }
 menu1
 menu(){
 cat<<EOF
  1. WEB01-10.0.0.7
  2. WEB02-10.0.0.8
  3. LB01-10.0.0.5
  4. LB02-10.0.0.6
  5. MySQL-10.0.0.51
  6. NFS-10.0.0.31
  7. 显示菜单
  8. 返回上一级菜单
 EOF
 }
 menu2(){
 cat<<EOF
  1.MySQL-10.0.0.51
  2.LB01-10.0.0.5
  3.显示菜单
  4.返回上一级菜单
 EOF
 }
 trap "" INT TSTP HUP#禁止ctrl+c ctrl+d等
 while true
 do
 read -p "选择你的岗位:[1|2|3]: " jd
  if [[ ! $jd =~ ^[0-9]+$ ]];then
  echo "必须输入数字"
  continue
  fi
 if [ $jd -eq 1 ];then
  while true
  do
  let i++
  read -p "请输入运维的口令: " op
  [ -z $op ] && echo "请输入正确的口令,不容许为空" && continue
  if [ $op = "woxiangjinqu" ];then
  action "口令正确欢迎运维大神归来" /bin/true
  break
  else
  action "口令不对从新输入!!!" /bin/false
  if [ $i -ge 3 ];then
  /root/game.sh
  fi
  fi
  done
  menu
 while true
 do
 read -p "请输入你要链接的服务器的编号[1|2|3|4菜单]: " num
 
 case $num in
   1)
  ssh $WEB01
  ;;
   2)
  ssh $WEB02
  ;;
   3)
  ssh $LB01
  ;;
   4)
  ssh $LB02
  ;;
   5)
  ssh $MySQL
  ;;
   7)
  clear
  menu
  ;;
   houmen)
  exit
  ;;
   8)
   menu1    
  break
  ;;
  *)
  echo "请输入正确的服务器编号"
 esac
 done
  elif [ $jd = 2 ];then
  while true
  do
  read -p "请输入开发的口令: " dev
  [ -z $dev ] && echo "请输入正确的口令,不容许为空" && continue
  if [ $dev = "wos" ];then
  action "口令正确欢迎代码搬运工" /bin/true
  break
  else
  action "口令不对从新输入!!!" /bin/false
  fi
  done  
  menu2
  while true
  do
  read -p "请输入链接的服务器编号:[1|2]: " num2 
case $num2 in 
1) 
ssh $MySQL 
;; 
2) 
ssh $LB01 
;; 
3)  
 clear 
 menu2 
;; 
4) 
 men1 
 break 
;; 
houmen) 
 exit 
;; 
*
echo "请输入正确的编号: [1|2]" 
esac 
done 
elif [ $jd = 4 ];then 
menu1 
fi 
done
------------------------------精简版----------------------------
[root@shell ~]# cat jump.sh
#!/bin/sh
. /etc/init.d/functions
WEB01=10.0.0.7
WEB02=10.0.0.8
LB01=172.16.1.5
LB02=10.0.0.6
MySQL=10.0.0.51
NFS=10.0.0.31
BACKUP=10.0.0.41
menu(){
cat<<EOF
						1. WEB01-10.0.0.7
						2. WEB02-10.0.0.8
						3. LB01-10.0.0.5
						4. LB02-10.0.0.6
						5. MySQL-10.0.0.51
						6. NFS-10.0.0.31
						7. 显示菜单
EOF
}
menu
trap "" INT TSTP HUP
while true
do
read -p "请输入你要链接的服务器的编号[1|2|3|4菜单]: " num
case $num in
	  1|web01)
		ssh $WEB01
	  ;;
	  2|web02)
		ssh $WEB02
	  ;;
	  3|lb01)
		ssh $LB01
	  ;;
	  4)
		ssh $LB02
	  ;;
	  5)
		ssh $MySQL
	  ;;
	  7)
		clear
		menu
	  ;;
		  houmen)
		exit
	  ;;
	  *) 
		echo "请输入正确的服务器编号"
esac
done
-------------------------------------------------------------

案例: nginx启动脚本

[root@shell ~]# /usr/sbin/nginx           # nginx启动方式
[root@shell ~]# /usr/sbin/nginx -s stop   # Nginx中止
[root@shell ~]# /usr/sbin/nginx -s reload # 重载
[root@shell ~]# /usr/sbin/nginx -s stop && /usr/sbin/nginx  # 先中止后启动
[root@shell ~]# cat start_nginx.sh
#!/bin/sh
. /etc/init.d/functions
case $1 in
	start)
		if [ `ps axu|grep nginx|grep master|wc -l` -eq 0 ];then
		 
			/usr/sbin/nginx
				if [ $? -eq 0 ];then
				action "Nginx $1 is"  /bin/true
				else
				action "Nginx $1 is"  /bin/false
				fi
	   else
			action "Nginx $1 is 已经在运行" /bin/true
	   fi
	;;
	stop)
	   /usr/sbin/nginx -s stop
	;;	
	reload)
	  /usr/sbin/nginx -s reload
	;;
	restart)
	 /usr/sbin/nginx -s stop
	sleep 1
	/usr/sbin/nginx
	;;
	status)  
		if [ `ps axu|grep nginx|grep master|wc -l` -eq 0 ];then
		echo "Nginx is not running......"
		else
		pid=`ps axu|grep nginx|grep master|awk '{print $2}'`
		port=`netstat -tnulp|grep nginx|grep master|grep 80|awk '{print $4}'`
		echo "当前的Nginx端口号为 $port"
		echo "当前的NgnxPID号为: $pid"
		fi
	;;
	*)
	 echo "USAGE: $0 [start|stop|restart|reload|status]"
esac

交互脚本expect

1.简单实现免交互登录
expect是一款自动化的脚本解释型的工具
脚本开头
expect脚本通常以#!/usr/bin/expect 开头,相似bash脚本。
经常使用后缀
expect脚本经常以.exp或者.ex结束。
expect主要命令
spawn 新建一个进程,这个进程的交互由expect控制
expect 等待接受进程返回的字符串,直到超时时间,根据规则决定下一步操做
send 发送字符串给expect控制的进程
set 设定变量为某个值      set user oldboy
exp_continue 从新执行expect命令分支
[lindex $argv 0] 获取expect脚本的第1个参数
[lindex $argv 1] 获取expect脚本的第2个参数
set timeout -1 设置超时方式为永远等待
set timeout 30 设置超时时间为30秒
interact 将脚本的控制权交给用户,用户可继续输入命令
expect eof 等待spawn进程结束后退出信号eof

安装expect

yum -y install expect
#!/usr/bin/expect
spawn ssh root@10.0.0.5
expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "centos\r" };
} 
interact

2.expect定义变量实现交互方式

#!/usr/bin/expect
set ip 10.0.0.5
set user root
set password centos
set timeout 5

spawn ssh $user@$ip

expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "$password\r" };
} 

#交互方式 interact 案例

批量获取在线主机, 进行秘钥批量分发

cat for_ip.sh 
#!/usr/bin/bash
#setup1 拿到IP地址
>ip.txt
for i in {1..10}
do
ip=10.0.0.$i
{
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip" >> ip.txt
 fi
 }&
done
#2.生成对应的密钥
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -P "" -f ~/.ssh/id_rsa
fi

#3.批量分发密钥

 while read line
        do
/usr/bin/expect << EOF
                        set pass 1
                        set timeout 2
                        spawn ssh-copy-id  $line -f 
                        expect {
                                "yes/no" { send "yes\r"; exp_continue}
                                "password:" { send "1\r"}
                        }
                        expect eof 
EOF
done<ip.txt

for 循环

语法结构: for 变量 in 变量值 变量值能够是数字 字母 变量 命令 do 执行的命令序列 具体的执行动做 能够和变量无关 done

for.sh

for i in 框子(苹果 梨子 香蕉 黄瓜)
do
	 echo $i 
	第一个循环:
	i=苹果
	echo 苹果
	第二次循环:
	i=梨子
	echo 梨子
	第四次循环
	i=黄瓜
	echo 黄瓜
done
[root@shell scripts]# cat for.sh 
for i in 1 10 20 30
do
	   echo $i
done
[root@shell scripts]# cat for.sh 
for i in `seq 10`
do
	   echo $i
done
[root@shell scripts]# cat for.sh
for i in `seq 5`
do
	   echo ok
done
案例: 数字从1加到100  笔试题
[root@shell scripts]# cat for.sh 
for i in `seq 100`
do
	   sum=$[$sum+$i]
done
echo $sum
[root@shell scripts]# cat for.sh
for i in $(seq 100)
do 
         sum=$(($sum+$i))  or   sum=$((sum+i))
done
echo $sum
[root@shell scripts]# seq -s + 100|bc
5050
扩展使用awk
[root@shell scripts]# seq 100|awk '{i=i+$1}END{print i}'
5050
[root@shell scripts]# echo {1..100}| awk -F " " '{for(i=1;i<NF+1;i++){j=j+i;}print j;}'
5050

案例: 批量pin 获取在线的IP地址 能ping通说明在线 
10.0.0.0/24  10.0.0.1-10.0.0.254

[root@shell scripts]# cat ping.sh 
#!/bin/sh
for i in {1..254}
do
	{
	IP=10.0.0.$i
	ping -c2 -W1 $IP &>/dev/null
	if [ $? -eq 0 ];then
	   echo "当前 $IP 在线"
	fi
	} &
done
wait
echo "完成在线取IP.........."
案例: 批量建立10个用户 用户前缀名oldboy oldboy1 oldboy2 oldboy3 ... oldboy10
要求判断用户是否存在 不存在则建立 存在提示用户已经存在  为每一个用户建立123456密码 而且判断密码是否建立成功
使用随机密码 而且把用户名和密码统一输出到文件中
[root@shell scripts]# vim user.sh 
#!/bin/sh
for i in {1..10}
do
		useradd oldboy$i
done
[root@shell scripts]# cat user.sh
#!/bin/bash
for i in {1..10}
do
      userdel -r oldboy$i
done

[root@shell scripts]# cat user1.sh
#!/bin/sh
for i in {1..10}
do
	id oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
	   echo "useradd: user oldboy$i already exists"
	else
	useradd   oldboy$i
	   if [ $? -eq 0 ];then
	   echo "Create oldboy$i is ok"
	   else
	   echo "Create oldboy$i is error"
	   fi
	fi
done

[root@shell scripts]# cat user2.sh
#!/bin/bash
for i in {1..10}
do 
id oldboy$i &>/dev/null
 if [ $? -eq 0 ];then
    echo "用户已经存在"
    else 
     useradd  oldboy$i
     if [ $? -eq 0 ];then
     echo "Create oldboy$i is ok"
     else
      echo "Create oldboy$i is error"
      fi
fi 
done
--------------------------
[root@shell scripts]# cat user.sh 
#!/bin/sh
> passwd.txt
for i in {1..10}
do
	pass=`date +%N|md5sum|cut -c1-8`
	id oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
	   echo "useradd: user oldboy$i already exists"
	else
	useradd   oldboy$i
	   if [ $? -eq 0 ];then
	   echo "Create oldboy$i is ok"
			echo $pass|passwd --stdin oldboy$i &>/dev/null
			if [ $? -eq 0 ];then
			echo "设置密码成功"
			else
			echo "设置密码失败"
			fi
		   echo  "oldboy$i $pass" >>passwd.txt
	   else
	   echo "Create oldboy$i is error"
	   fi
	fi
done
用户的前缀手动输入 建立的个数手动输入 for循环 结合case语句方式批量建立用户
[root@shell scripts]# cat user1.sh 
#!/bin/sh
read -p "请输入用户的前缀名称: " prefix
[ -z $prefix ] && echo "请输入用户前缀" && exit
read -p "请输入建立用户的个数: " num
if [[ ! $num =~ ^[0-9]+$ ]];then
	echo "请输入整数"
fi
for i in `seq $num`
do
	echo $prefix$i
done
read -p "你肯定要建立以上的用户吗?[y|Y|yes|n|N|no]: " re
for a in `seq $num`
do
		  user=$prefix$a
case $re in
	  y|Y|yes)
			 id $user &>/dev/null
		 if [ $? -eq 0 ];then
			echo "用户存在"
		 else
			useradd $user &>/dev/null
			if [ $? -eq 0 ];then
			   echo "用户建立成功"
			else
			   echo "用户建立失败"
			fi
		 fi
		 ;;
	   n|N|no)
		  exit
		;;
		   *)
		  echo "USAGE: $0 [请输入前缀个数....]"

esac
done

while循环

语法格式: while 条件表达式 为真则执行 不然不执行 do 命令序列 done

[root@shell scripts]# cat while.sh
while true
do
	 echo ok
	 sleep 2
done
[root@shell scripts]# cat while.sh 
a=0
while [ $a -le 3 ]
do
	 let a++
	 echo ok
done
[root@shell scripts]# sh while1.sh 
ok
ok
ok
ok
[root@shell scripts]# cat while1.sh
a=1
while [ $a -le 3 ]
do 
  let a++
       echo ok
done
[root@shell scripts]# sh while1.sh 
ok
ok
ok
使用while循环从1加到100
[root@shell scripts]# cat while.sh 
a=1
while [ $a -le 100 ]
do
	 sum=$[$sum+$a]
	 let a++
done
echo $sum
while读取文件
	  for循环取值方式: 按照空格取值
		  [root@shell scripts]# cat test.sh
			#!/bin/sh
			for i in `cat /etc/hosts`
			do
				echo $i
			done
	  while循环取值方式: 按照行取值
		  while read line
			do
				echo $line
			done</etc/hosts
案例: 经过读取文件的方式建立用户名
    user.txt 
	zs1
	old2
	tes1
	test2
	lisi
[root@shell scripts]# cat while.sh 
#!/bin/sh
while read line
do
		user=`echo $line|awk '{print $1}'`
	pass=`echo $line|awk '{print $2}'`
	useradd $user
	echo $pass|passwd --stdin $user
done<user.txt

流程控制语句 exit 退出当前的脚本 break 跳出本层循环 continue 省略当前循环的后面的代码.从头开始执行

#!/bin/sh
while true
do
		echo ok
		continue
		echo hehe
done
echo done...............

exit 退出当前整个脚本
[root@shell scripts]# cat while2.sh
#!/bin/sh
for i in `seq 10`
do
	useradd oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
		echo "Create oldboy$i success"
		else
		exit
	fi
done
break 跳出本层循环 
[root@shell scripts]# cat while2.sh
#!/bin/sh
for i in `seq 10`
do
	useradd oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
		echo "Create oldboy$i success"
		else
		break
	fi
done
echo done................
[root@shell scripts]# sh while2.sh 
Create oldboy1 success
Create oldboy2 success
Create oldboy3 success
Create oldboy4 success
done................
 continue
   [root@shell scripts]# cat while2.sh 
	#!/bin/sh
	for i in `seq 10`
	do
		useradd oldboy$i &>/dev/null
			if [ $? -eq 0 ];then
			echo "Create oldboy$i success"
			else
			continue
		fi
	done
	echo done................
  [root@shell scripts]# sh while2.sh 
	Create oldboy1 success
	Create oldboy2 success
	Create oldboy3 success
	Create oldboy4 success
	Create oldboy6 success
	Create oldboy7 success
	Create oldboy8 success
	Create oldboy9 success
	Create oldboy10 success
	done................

fun函数

1.命令的集合,完成特定功能的代码块 ​ 2.便于代码复用 3.和变量相似 只能先定义 在调用,只定义不调用 不执行 函数的定义: [root@shell scripts]# cat fun.sh #!/bin/sh fun(){ echo "函数的第一种定义方式" }

function fun1(){
	echo "函数的第二种定义方式"
}

function fun2 {
	echo "函数的第三种定义方式"
}
fun
fun1
fun2
[root@shell scripts]# sh fun.sh 
函数的第一种定义方式
函数的第二种定义方式
函数的第三种定义方式

函数的传参 函数的传参第一种方式 在函数的后面调用

[root@shell scripts]# cat fun1.sh
	#!/bin/sh
	fun(){
		if [ -f $1 ];then
		  echo "文件存在"
		else
		  echo "文件不存在"
		fi
	}
	fun /etc/hosts
	[root@shell scripts]# sh -x fun1.sh /etc/passwd 
	+ fun /etc/hosts
	+ '[' -f /etc/hosts ']'
	+ echo 文件存在
	文件存在
[root@shell scripts]# cat fun1.sh
#!/bin/sh
fun(){
	if [ -f $1 ];then
	  echo "$1 文件存在"
	else
	  echo "$1 文件不存在"
	fi
}
fun $2
[root@shell scripts]# sh fun1.sh /etc/passwd /etc/hosts
/etc/hosts 文件存在

-------------------------
[root@shell scripts]# cat fun1.sh
#!/bin/sh
fun(){
	if [ -f $1 ];then
	  echo "$1 文件存在"
	else
	  echo "$1 文件不存在"
	fi
}
fun $2 $1
[root@shell scripts]# sh fun1.sh /etc/passwd /etc/hosts
/etc/hosts 文件存在

[root@shell scripts]# cat fun1.sh
#!/bin/sh
fun(){
	if [ -f $2 ];then
	  echo "$2 文件存在"
	else
	  echo "$2 文件不存在"
	fi
}
fun $2 $1
[root@shell scripts]# sh fun1.sh /etc/passwd /etc/hosts
/etc/passwd 文件存在
函数传参的第二种方式:
    调用当前shell中的变量
[root@shell scripts]# cat fun1.sh
#!/bin/sh
file=$1
fun(){
	if [ -f $file ];then
	  echo "$file 文件存在"
	else
	  echo "$file 文件不存在"
	fi
}
fun

函数的本地变量: 只能在函数体中生效 在其余的shell语句中不生效
local file=/etc/hosts
[root@shell scripts]# vim fun1.sh 
#!/bin/sh
fun(){
	local file=/etc/hosts
	if [ -f $file ];then
	  echo "$file 文件存在"
	else
	  echo "$file 文件不存在"
	fi
}
fun
echo $file
案例:
[root@shell scripts]# cat fun2.sh 
#!/bin/sh
num1=10
fun(){
	for i in `seq 10`
	do
	total=$[$i+$num1]
	done
}
fun
echo $total
[root@shell scripts]# sh fun2.sh
20	

注意变量位置
[root@shell scripts]# cat fun2.sh
#!/bin/sh
fun(){
	for i in `seq 10`
	do
	total=$[$i+$num1]
	done
}
num1=10
fun
echo $total
[root@shell scripts]# sh fun2.sh
20
[root@shell scripts]# cat fun2.sh
#!/bin/sh
fun(){
	for i in `seq 10`
	do
	total=$[$i+$1]
	done
	echo $total
}
fun $1
fun $2
fun $3
[root@shell scripts]# sh fun2.sh 10 20 30
20
30
40
 [root@shell scripts]# cat fun2.sh 
  #!/bin/sh
  fun(){
  for i in `seq 10`
  do
  total=$[$i+$1]
  done
  echo $total
  }
  fun $2 $1 $3
  [root@shell scripts]# sh fun2.sh 20 30 10
  40
 
 函数的返回值: return
    [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
  if [ -f $file ];then
   return 50
  else
   return 100
  fi
  }
  fun
  echo $?
  [root@shell scripts]# sh fun1.sh /etc/hostssss
  100
  [root@shell scripts]# sh fun1.sh /etc/hosts
  50
 
    [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
  if [ -f $file ];then
   return 50
  else
   return 100
  fi
  }
  fun
  if [ $? -eq 50 ];then
  echo "文件存在"
  else
  echo "文件不存在"
  fi
  [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
   echo 50
   return 100
  }
  num=`fun`
  echo "当前函数的返回值为: $?"
  echo "当前函数的执行结果为: $num"
  [root@shell scripts]# sh fun1.sh /etc/hosts
  当前函数的返回值为: 100
  当前函数的执行结果为: 50
  [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
   echo 50
   return 100
  }
  num=`fun`
  name=oldboy
  echo "当前函数的返回值为: $?"
  echo "当前函数的执行结果为: $num"
  [root@shell scripts]# sh fun1.sh /etc/hosts
  当前函数的返回值为: 0
  当前函数的执行结果为: 50
 
 函数在其余shell中调用 相似变量
    [root@shell scripts]# cat fun.sh
  /bin/sh
  fun(){
  echo "函数的第一种定义方式"
  }
 function fun1(){
  echo "函数的第二种定义方式"
 }
 
 function fun2 {
  echo "函数的第三种定义方式"
 }
 [root@shell scripts]# fun
 -bash: fun: command not found
 [root@shell scripts]# . fun.sh
 [root@shell scripts]# fun
 函数的第一种定义方式
 [root@shell scripts]# fun1
 函数的第二种定义方式
 [root@shell scripts]# fun2
 函数的第三种定义方式

数组

1.数组的分类

普通数组

只能使用整数来定义

关联数组

可以使用数字和字符来定义

变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间,至关于多个变量的集合
数组名和索引
索引:编号从0开始,属于数值索引
注意:索引可支持使用自定义的格式,而不只是数值格式,即为关联索引,bash4.0版本以后开始支持
bash的数组支持稀疏格式(索引不连续)
声明数组:
declare -a ARRAY_NAME
declare -A ARRAY_NAME 关联数组
注意:二者不可相互转换
高级变量用法-有类型变量
Shell变量通常是无类型的,可是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两个命令是等价的
declare [选项] 变量名
-r 声明或显示只读变量
-i 将变量定义为整型数
-a 将变量定义为数组
-A 将变量定义为关联数组
-f 显示已定义的全部函数名及其内容
-F 仅显示已定义的全部函数名
-x 声明或显示环境变量和函数
-l 声明变量为小写字母 declare –l var=UPPER
-u 声明变量为大写字母 declare –u var=lower

间接变量引用

bash Shell提供了两种格式实现间接变量引用
eval tempvar=\$$variable1
tempvar=${!variable1}
示例:
[root@server ~]# N=NAME
[root@server ~]# NAME=wangxiaochun
[root@server ~]# N1=${!N}
[root@server ~]# echo $N1
wangxiaochun
[root@server ~]# eval N2=\$$N
[root@server ~]# echo $N2
wangxiaochun

建立临时文件

mktemp命令:建立并显示临时文件,可避免冲突
mktemp [OPTION]... [TEMPLATE]
TEMPLATE: filenameXXX
X至少要出现三个
OPTION:
-d: 建立临时目录
-p DIR或--tmpdir=DIR:指明临时文件所存放目录位置
示例:
mktemp /tmp/testXXX
tmpdir=`mktemp –d /tmp/testdirXXX`
mktemp --tmpdir=/testdir testXXXXXX

安装复制文件

install命令:
install [OPTION]... [-T] SOURCE DEST 单文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...建立空目录
选项:
-m MODE,默认755
-o OWNER
-g GROUP
示例:
install -m 700 -o wang -g admins srcfile desfile
install –m 770 –d /testdir/installdir

2.数组的格式

变量名称=变量的值 数组名称[索引]=(元素的值)

3.定义数组

第一种定义方式

索引定义

默认索引从0开始

  [root@shell scripts]# array[1]=cm
	[root@shell scripts]# array[5]=zs
	[root@shell scripts]# array[6]=ls

第二种定义方式

一次性定义多个值

 [root@shell scripts]# array=(ll lz lw lz)

第三种定义方式

混合定义的方式

 [root@shell scripts]# array=(qq aa cc [20]=tt [22]=yy)

第四种定义方式

命令定义

  [root@shell scripts]# ls /tmp/
	nging.pid  vmware-root_6223-1681855427
	[root@shell scripts]# array=(`ls /tmp`)

4.调用数组 declare -a 查看当前定义全部的普通数组

 [root@shell scripts]# echo $array    # 默认只能查看下标为0的值
 mysql
 [root@shell scripts]#
 [root@shell scripts]# echo ${array[*]} # 查看数组中的全部内容
 mysql shell redis
 [root@shell scripts]# echo ${array[@]}
 mysql shell redis
 [root@shell scripts]# echo ${!array[@]} # 查看全部的下标
 0 1 2
 [root@shell scripts]# echo ${array[1]} # 经过下标查看值
 shell
 [root@shell scripts]# echo ${array[2]}
 redis
 [root@shell scripts]# echo ${array[0]}
 mysql
 [root@shell scripts]# echo ${#array[*]} # 查看元素的个数
 3
 案例: 判断当前公司中几台服务器的IP地址是否能ping通

61.135.169.121 123.126.55.41 10.0.0.254 8.8.8.8

定义数组:

 [root@shell scripts]# IP=(61.135.169.121 123.126.55.41 10.0.0.254 8.8.8.8)

获取数组的值:

  [root@shell scripts]# echo ${IP[*]}
    61.135.169.121 123.126.55.41 10.0.0.254 8.8.8.8

for循环方式去ping每一个IP

  [root@shell scripts]# for i in ${IP[*]};do echo $i;done
  61.135.169.121
  123.126.55.41
  10.0.0.254
  8.8.8.8
  [root@shell scripts]# for i in ${IP[*]};do ping -c2 -W1 $i;done
  [root@shell scripts]# cat array.sh
  #!/bin/sh
  IP=(
  61.135.169.121
  123.126.55.41
  10.0.0.254
  8.8.8.8
  10.0.0.51
  )
  for i in ${IP[*]}
  do
  ping -c2 -W1 $i &>/dev/null
  if [ $? -eq 0 ];then
    echo "ping $i is ok............"
  else
    echo "ping $i is error........."
  fi
  done

关联数组: 下标可使用数字和字符串 声明关联数组: declare -A 数组名称 使用下标方式定义数组

	[root@shell scripts]# array[index1]=redis
	[root@shell scripts]# array[index2]=mysql
	[root@shell scripts]# array[index3]=shell
	[root@shell scripts]# echo ${array[*]}
	redis mysql shell
第二种方式定义数组
[root@shell scripts]# array=([0]=zabbix [index4]=kvm [index5]=mongo)
[root@shell scripts]# echo $array[*]
zabbix[*]
[root@shell scripts]# echo ${array[*]}
kvm mongo zabbix
[root@shell scripts]# echo ${!array[*]}
index4 index5 0

关联数组案例: 统计文件中性别出现的次数

 cat sex.txt
 m
 f
 f
 m
 m
 m
 x
 [root@shell scripts]# sort sex.txt|uniq -c
 2 f
 4 m
 1 x
 [root@shell scripts]# sort sex.txt|uniq -c|sort -rn
 4 m
 2 f
 1 x
 [root@shell scripts]# cat sex.txt |uniq -c|sort -rn
 3 m
 2 f
 1 x
 1 m
 [root@shell scripts]# cat sex.txt |sort
 f
 f
 m
 m
 m
 m
 x
 [root@shell scripts]# cat sex.txt |sort |uniq -c
 2 f
 4 m
 1 x
 [root@shell scripts]# cat sex.txt |sort |uniq -c|sort -rn
 4 m
 2 f
 1 x
 [root@shell scripts]# cat s.sh
 for i in `cat sex.txt`
 do
 if [ $i = m ];then
 let m++
 elif [ $i = f ];then
 let f++
 else
 let x++
 fi
 done
 echo "m出现了 $m 次"
 echo "f出现了 $f 次"
 echo "x出现了 $x 次"
 [root@shell scripts]# sh s.sh
 m出现了 4 次
 f出现了 2 次
 
 x出现了 1 次
 --------------------------
 
  [root@shell scripts]# let a++
  [root@shell scripts]# echo $a
  1
  [root@shell scripts]# let array[b]++
  [root@shell scripts]# echo ${array[b]}
  1
  [root@shell scripts]# let a++
  [root@shell scripts]# let b++
  [root@shell scripts]# let b++
  [root@shell scripts]# let a++
  [root@shell scripts]# let array[b]++
  [root@shell scripts]# let array[a]++
  [root@shell scripts]# let array[a]++
  [root@shell scripts]#
  [root@shell scripts]# echo $a
  3
  [root@shell scripts]# echo ${array[b]}
  2
  [root@shell scripts]# echo ${array[a]}
 
  2
 -----------------------------
 
    cat s.sh # 执行过程
    declare -A sex
  for i in `cat sex.txt`
  do
    let sex[$i]++
 
 --------------
 
    i=m
    let sex[m]++
 
 ---
 
    i=f
    let sex[f]++
 
 ---
 
    i=m
    let sex[m]++
  done
  echo "m出现了 $m 次"
  echo "f出现了 $f 次"
  echo "x出现了 $x 次"
  [root@shell scripts]# cat s.sh
  declare -A sex
  for i in `cat sex.txt`
  do
    let sex[$i]++
  done
  echo "m出现了 ${sex[m]} 次"
  echo "f出现了 ${sex[f]} 次"
  echo "x出现了 ${sex[x]} 次"
 
 •   [root@shell scripts]# sh s.sh
 •    m出现了 4 次
 •    f出现了 2 次
 •    x出现了 1 次
 
    遍历数组:
    [root@shell scripts]# cat s.sh
  declare -A sex
  for i in `cat sex.txt`
  do
    let sex[$i]++
  done
  #echo "m出现了 ${sex[m]} 次"
  #echo "f出现了 ${sex[f]} 次"
  #echo "x出现了 ${sex[x]} 次"
  for a in ${!sex[*]}
  do
  echo "$a 出现了 ${sex[$a]} 次"
  done
  [root@shell scripts]# sh s.sh
  f 出现了 2 次
  m 出现了 4 次
  x 出现了 1 次
 案例: 统计/etc/passwd 中出现的解释器
  declare -A bash # 执行过程
  while read line
  do
    let bash[`echo $line|awk -F: '{print $NF}'`]++
    root:x:0:0:root:/root:/bin/bash
    let bash[/bin/bash]++
  bin:x:1:1:bin:/bin:/sbin/nologin 
  let bash[/sbin/nologin]++ 
 
done</etc/passwd 
for a in ${!bash[*]} 
do 
echo "$a 出现了 ${bash[$a]} 次" 
done 
[root@shell scripts]# cat bash.sh  
declare -A bash 
while read line 
do 
  let bash[`echo $line|awk -F: '{print $NF}'`]++ 
done</etc/passwd 
for a in ${!bash[*]} 
do 
echo "$a 出现了 ${bash[$a]} 次" 
done 
[root@shell scripts]# sh bash.sh 
/sbin/nologin 出现了 21 次 
/bin/sync 出现了 1 次 
/bin/bash 出现了 26 次 
/sbin/shutdown 出现了 1 次 
/sbin/halt 出现了 1 次

统计当前nginx日志文件中IP地址出现的次数

  [root@shell scripts]# cat ip.sh
  #!/bin/sh
  declare -A IP
  while read line
  do
  let IP[`echo $line|awk '{print $1}'`]++
  done</var/log/nginx/access.log
  for i in ${!IP[*]}
  do
  echo "$i 出现了 ${IP[$i]} 次"
  done

数组的删除

 引用数组
 引用数组元素
 ${ARRAY_NAME[INDEX]}
 注意:省略[INDEX]表示引用下标为0的元素
 引用数组全部元素
 ${ARRAY_NAME[*]}
 ${ARRAY_NAME[@]}
 数组的长度(数组中元素的个数)
 ${#ARRAY_NAME[*]}
 ${#ARRAY_NAME[@]}
 除数组中的某元素:致使稀疏格式
 unset ARRAY[INDEX]
 删除整个数组
 unset ARRAY
 [root@localhost ~]# unset array[1]
 [root@localhost ~]# echo ${array[*]}
 one three four
 [root@localhost ~]# unset array
 [root@localhost ~]# echo ${array[*]}

数组内容截取和替换

数组数据处理 引用数组中的元素:

 数组切片:
 ${ARRAY[@]:offset:number}
 offset 要跳过的元素个数
 number 要取出的元素个数
 取偏移量以后的全部元素 ${ARRAY[@]:offset}
 向数组中追加元素:
 ARRAY[${#ARRAY[*]}]=value
 联数组:
 declare -A ARRAY_NAME
 ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
 注意:关联数组必须先声明再调用
    [root@localhost ~]# array=(0 1 2 3 4)
    [root@localhost ~]# echo ${array[@]:1:3}
    1 2 3
    [root@localhost ~]# array=($(echo {a..z}))
    [root@localhost ~]# echo ${array[@]}
    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@localhost ~]# echo ${array[@]:1:3}
    b c d
    [root@localhost ~]# array=(1 2 3 4 5)
    [root@localhost ~]# echo ${array[@]/3/three}
    1 2 three 4 5

字符串增删改查匹配处理

基于模式取子串
${var#*word}:其中word能够是指定的任意字符
功能:自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的全部字符
${var##*word}:同上,贪婪模式,不一样的是,删除的是字符串开头至最后一次由word指定的字符之间的全部内容
示例:
file=“var/log/messages”
${file#*/}: log/messages
${file##*/}: messages
 ${var%word*}:其中word能够是指定的任意字符
功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符串(含)之间的全部字符
file="/var/log/messages"
${file%/*}: /var/log
${var%%word*}:同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的全部字符
示例:
url=http://www.magedu.com:80
${url##*:} 80
${url%%:*} http       
 查找并删除
${var/pattern}:删除var表示的字符串中第一次被pattern匹配到的字符串
${var//pattern}:删除var表示的字符串中全部被pattern匹配到的字符串
${var/#pattern}:删除var表示的字符串中全部以pattern为行首匹配到的字符串
${var/%pattern}:删除var所表示的字符串中全部以pattern为行尾所匹配到的字符串
字符大小写转换
${var^^}:把var中的全部小写字母转换为大写
${var,,}:把var中的全部大写字母转换为小写            

shell正则应用

第1章 shell正则应用

1.1 基础正则表达式

1.2 grep正则表达式实战

1.2.1 过滤以m开头的行

1.2.2 过滤以m结尾的行

1.2.3 排除空行, 并打印行号

1.2.4 匹配任意一个字符,不包括空行

1.2.5 匹配全部

1.2.6 匹配单个任意字符

1.2.7 以点结尾

1.2.8 精确匹配到

[1.2.9 匹配有abc的行

[1.2.10 匹配数字所在的行"0-9]"

[1.2.11 匹配全部小写字母a-z]

1.2.12 重复8三次

1.2.13 重复数字8, 3-5次

1.2.14 至少1次或1次以上

1.3 sed文本处理

1.3.1 sed命令格式

1.3.2 sed正则使用

1.3.3 sed选项参数

1.3.4 sed`内置命令参数

1.3.5 先删除行,而后管道给后面的sed进行替换

1.3.6 使用-e进行屡次编辑修改操做

1.3.7 打印命令p.

1.3.8 追加命令a

1.3.9 修改命令c

1.3.10 删除命令d

1.3.11 插入命令i

1.3.12 写文件命令w

1.3.13 获取下一行命令n

1.3.14 暂存和取用命令h H g G

1.3.15 反向选择命令!

1.3.16 sed匹配替换...

1.3.17 删除文件...

1.3.18 给文件行添加注释...

1.3.19 添加#注释符...

1.4 Awk文本处理...

1.4.1 awk 的处理数据的方式...

1.4.2 awk的语法格式...

1.4.3 选项 options.

1.4.4 awk命令格式...

1.4.5 Awk工做原理...

1.4.6 Awk内部变量...

1.4.7 Awk模式动做...

1.4.8 awk示例1.

1.4.9 awk示例2.

[1.4.10 Awk条件判断... ]

1.4.11 Awk循环语句...

1.4.12 awk数组概述...

[1.4.13 Awk数组案例... ]

第1章 shell正则应用

正则表达式regular expression, RE是一种字符模式,用于在查找过程当中匹配指定的字符。

在大多数程序里,正则表达式都被置于两个正斜杠之间;例如/l[oO]ve/就是由正斜杠界定的正则表达式,它将匹配被查找的行中任何位置出现的相同模式。在正则表达式中,元字符是最重要的概念。

正则表达式的做用

>1.Linux正则表达式grep,sed,awk

2.大量的字符串文件须要进行配置,并且是非交互式的

3.过滤相关的字符串,匹配字符串,打印字符串

正则表达式注意事项

>1.正则表达式应用很是普遍,存在于各类语言中,例如:php,python,java等。

2.正则表达式和通配符特殊字符是有本质区别的。

3.要想学好grep、sed、awk首先就要掌握正则表达式。

1.1 基础正则表达式

元字符意义BRE,正则表达式实际就是一些特殊字符,赋予了他特定的含义。

正则表达式 描述

\    转义符,将特殊字符进行转义,忽略其特殊意义

^    匹配行首,awk中,^则是匹配字符串的开始 

$    匹配行尾,awk中,$则是匹配字符串的结尾

^$   表示空行

.    匹配除换行符\n以外的任意单个字符

.*   匹配全部

[ ]   匹配包含在[字符]之中的任意一个字符

[^ ]  匹配[^字符]以外的任意一个字符

[ - ]  匹配[]中指定范围内的任意一个字符

?    匹配以前的项1次或者0次

\+    匹配以前的项1次或者屡次

\*    匹配以前的项0次或者屡次, .*

()   匹配表达式,建立一个用于匹配的子串

{ n }  匹配以前的项n次,n是能够为0的正整数

{n,}  以前的项至少须要匹配n次

{n,m}  指定以前的项至少匹配n次,最多匹配m次,n<=m

|    交替匹配|两边的任意一项ab(c|d)匹配abc或abd

特定字符:

>[[:space:]]  空格
[[:digit:]]   [0-9]
[[:lower:]]   [a-z]
[[:upper:]]   [A-Z]
[[:alpha:]]   [a-Z]

1.2 grep正则表达式实战

I am lizhenya teacher!
I teach linux.
test

I like badminton ball ,billiard ball and chinese chess!
my blog is http: blog.51cto.com
our site is http:www.lizhenya.com
my qq num is 593528156
not 572891888887. 

1.2.1 过滤以m开头的行

[root@Shell ~]#** grep "^m" test.txt

my blog is http: blog.51cto.com

my qq num is 572891887.

1.2.2 过滤以m结尾的行

[root@Shell ~]#** grep "m$" test.txt

my blog is http: blog.51cto.com

our site is http:www.lizhenya.com

1.2.3 排除空行, 并打印行号

[root@student ~]#grep -vn "^$" lizhenya.txt

1.2.4 匹配任意一个字符,不包括空行

[root@student ~]# grep "." lizhenya.txt

1.2.5 匹配全部

[root@student ~]# grep ".*" lizhenya.txt

1.2.6 匹配单个任意字符

 [root@node1 ~]# grep "lizhen.a" lizhenya.txt

1.2.7 以点结尾的

 [root@student ~]# grep "\.$" lizhenya.txt

1.2.8 精确匹配到

 [root@student ~]# grep -o "8*" lizhenya.txt

1.2.9 匹配有abc的行

 [root@student ~]# grep "[abc]" lizhenya.txt

1.2.10 匹配数字所在的行"[0-9]"

 [root@student ~]# grep "[0-9]" lizhenya.txt

1.2.11 匹配全部小写字母[a-z]

 [root@student ~]# grep "[a-z]" lizhenya.txt

1.2.12 重复8三次

 [root@student ~]# grep "8\{3\}" lizhenya.txt

1.2.13 重复数字8, 3-5次

 [root@student ~]# grep -E "8{3,5}" test.txt

1.2.14 至少1次或1次以上

 [root@student ~]# grep -E "8{1,}" lizhenya.txt

1.3 sed文本处理

sed是一个流编辑器, 非交互式的编辑器,它一次处理一行内容.

处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space)

接着用 sed 命令处理缓冲区中的内容,处理完成后, 把缓冲区的内容送往屏幕。

接着处理下一行,这样不断重复,直到文件末尾。

文件内容并无改变,除非你 使用重定向存储输出。

Sed 要用来自动编辑一个或多个文件;简化对文件的反复操做;编写转换程序等。

1.3.1 sed命令格式

sed [options] 'command' file(s)

1.3.2 sed正则使用

与 grep同样,sed 在文件中查找模式时也可使用正则表达式(RE)和各类元字符。

正则表达式是括在斜杠间的模式,用于查找和替换,如下是sed支持的元字符。

使用基本元字符集 ^, $, ., *, [], [^], < >, (), {}

使用扩展元字符集 ?, +, { }, |, ( )

使用扩展元字符的方式 + sed -r

1.3.3 sed选项参数

-e 容许多项编辑

-n 取消默认的输出

-i 直接修改对应文件

-r 支持扩展元字符

1.3.4 sed`内置命令参数

a 在当前行后添加一行或多行

c 在当前行进行替换修改

d 在当前行进行删除操做

i 在当前行以前插入文本

p 打印匹配的行或指定行

n 读入下一输入行,从下一条命令进行处理

! 对所选行之外的全部行应用命令

h 把模式空间里的内容重定向到暂存缓冲区

H 把模式空间里的内容追加到暂存缓冲区

g 取出暂存缓冲区的内容,将其复制到模式空间,覆盖该处原有内容

G 取出暂存缓冲区的内容,将其复制到模式空间,追加在原有内容后面

1.3.5 先删除行,而后管道给后面的sed进行替换

[root@Shell ~]# sed '1,9d' passwd |sed 's#root#alex#g'

1.3.6 使用-e进行屡次编辑修改操做

[root@Shell ~]# sed -e '1,9d' -e 's#root#alex#g' passwd

1.3.7 打印命令p

1.3.7.1 打印匹配halt的行

[root@Shell ~]# sed -n '/halt/p' passwd

1.3.7.2 打印第二行的内容

[root@Shell ~]#** sed -n '2p' passwd
bin:x:1:1:bin:/bin:/sbin/nologin

1.3.7.3 打印最后一行

[root@Shell ~]# sed -n '$p' passwd

1.3.8 追加命令a

1.3.8.1 给30行添加配置 \t tab键(须要转义) \n 换行符

[root@Shell ~]# sed -i '30a listen 80;' passwd

1.3.9 修改命令c

1.3.9.1 指定某行进行内容替换

[root@Shell ~]# sed -i '7c SELINUX=Disabled' /etc/selinux/config

1.3.9.2 正则匹配对应内容, 而后进行替换

[root@Shell ~]# sed -i '/^SELINUX=/c SELINUX=Disabled' /etc/selinux/config

1.3.9.3 非交互式修改指定的配置文件

[root@Shell ~]# sed -ri '/UseDNS/cUseDNS no' /etc/ssh/sshd_config
[root@Shell ~]# sed -ri '/GSSAPIAuthentication/c#GSSAPIAuthentication no' /etc/ssh/sshd_config
[root@Shell ~]# sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config

1.3.10 删除命令d

1.3.10.1 指定删除第三行, 但不会改变文件内容

[root@Shell ~]# sed '3d' passwd
[root@Shell ~]# sed '3{d}' passwd

1.3.10.2 从第三行删除到最后一行

[root@Shell ~]# sed '3,$d' passwd

1.3.10.3 删除最后一行

[root@Shell ~]# sed '$d' passwd

1.3.10.4 删除全部的行

[root@Shell ~]# sed '1,$d' passwd

1.3.10.5 匹配正则进行该行删除

[root@Shell ~]# sed /mail/d passwd

1.3.11 插入命令i

1.3.11.1 在文件的某一行上面添加内容

[root@Shell ~]# sed -i '30i listen 80;' passwd

1.3.12 写文件命令w

1.3.12.1 将匹配到的行写入到新文件中

[root@Shell ~]# sed -n '/root/w newfile' passwd

1.3.12.2 将passwd文件的第二行写入到newfile中

[root@Shell ~]# sed -n '2w newfile' passwd

1.3.13 获取下一行命令n

1.3.13.1 匹配root的行, 删除root行的下一列

[root@Shell ~]# sed '/root/{n;d}' passwd

1.3.13.2 替换匹配root行的下一列

[root@Shell ~]# sed '/root/{n; s/bin/test/}' passwd

1.3.14 暂存和取用命令h H g G

g: 将hold space中的内容拷贝到pattern space中,原来pattern space里的内容被覆盖
G:将hold space中的内容append到pattern space\n后
h: 将pattern space中的内容拷贝到hold space中,原来hold space里的内容被覆盖
H: 将pattern space中的内容append到hold space\n后
d: 删除pattern中的全部行,并读入下一新行到pattern中
D: 删除multiline pattern中的第一行,读入下一行

1.3.14.1 将第一行的写入到暂存区, 替换最后一行的内容

[root@Shell ~]# sed '1h;$g' /etc/hosts

1.3.14.2 将第一行的写入到暂存区, 在最后一行调用暂存区的内容

[root@Shell ~]# sed '1h;$G' /etc/hosts

1.3.14.3 将第一行的内容删除但保留至暂存区, 在最后一行调用暂存区内容追加至于尾部

[root@Shell ~]# sed -r '1{h;d};$G' /etc/hosts

QQ图片20201022091844

1.3.14.4 将第一行的内容写入至暂存区, 从第二行开始进行重定向替换

[root@Shell ~]# sed -r '1h;2,$g' /etc/hosts

1.3.14.5 将第一行重定向至暂存区, 2-3行追加至暂存区, 最后追加调用暂存区的内容

[root@Shell ~]# sed -r '1h; 2,3H; $G' /etc/hosts

4 图解sed ‘1!G;h;$!d’ file

1 sed简介

sed是面向流的行编辑器。所谓面向流,是指接受标准输入的输入,输出内容到标准输出上。sed编辑器逐行处理文件(或输入),并将结果发送到屏幕。

具体过程以下: sed将处理的行读入到一个临时缓存区中(也称为模式空间pattern space),sed中的命令依次执行,直到全部命令执行完毕,完成后把该行发送到屏幕上,清理pattern space中的内容;接着重复刚才的动做,读入下一行,直到文件处理结束。

sed每处理完一行就将其从pattern space中删除,而后将下一行读入,进行处理和显示。处理完输入 文件的最后一行后,sed便结束运行。sed把每一行都存在临时缓冲区中,对这个副本进行编辑,因此不会修改原文件。

2 sed执行流程图

image-20201022145834344

3 什么是Pattern Space,Hold Space

Pattern Space至关于车间,sed把流内容在这里进行处理,Hold Space至关于仓库,加工的半成品在这里进行临时存储。

因为各类缘由,好比用户但愿在某个条件下脚本中的某个命令被执行,或者但愿模式空间获得保存以便下一次处理,都有可能使得sed在处理文件的时候不按照正常的流程来进行。这个时候,sed设置了一些高级命令来知足用户的要求。

一些高级命令

g: 将hold space中的内容拷贝到pattern space中,原来pattern space里的内容被覆盖

G:将hold space中的内容append到pattern space\n后

h: 将pattern space中的内容拷贝到hold space中,原来hold space里的内容被覆盖

H: 将pattern space中的内容append到hold space\n后

d: 删除pattern中的全部行,并读入下一新行到pattern中

D: 删除multiline pattern中的第一行,不读入下一行

4 图解sed ‘1!G;h;$!d’ file

1!G 第一行不执行G命令,从第二行开始执行

$!d 最后一行不删除

[root@localhost test]# cat file
1 1 1
2 2 2
3 3 3
[root@localhost test]# sed '1!G;h;$!d' file
3 3 3
2 2 2
1 1 1

图中P表明Pattern Space,H表明Hold Space。绿色表明pattern space中的数据,蓝色表明hold space中的数据。

1.3.15 反向选择命令!

1.3.15.1 除了第三行,其余所有删除

[root@Shell ~]# sed -r '3!d' /etc/hosts

1.3.16 sed匹配替换

s 替换命令标志

g 行内全局替换

i 忽略替换大小写

替换命令s

1.3.16.1 替换每行出现的第一个root

[root@Shell ~]# sed 's/root/alice/' passwd

1.3.16.2 替换以root开头的行

[root@Shell ~]# sed 's/^root/alice/' passwd

1.3.16.3 查找匹配到的行, 在匹配的行后面添加内容

[root@Shell ~]# sed -r 's/[0-9][0-9]$/& .5/' passwd

1.3.16.4 匹配包含有root的行进行替换

[root@Shell ~]# sed -r 's/root/alice/g' passwd

1.3.16.5 匹配包含有root的行进行替换,忽略大小写

[root@Shell ~]# sed -r 's/root/alice/gi' /etc/passwd

1.3.16.6 后向引用

[root@Shell ~]# sed -r 's#(Roo)#\1-alice#g' passwd
[root@Shell ~]# ifconfig eth0|sed -n '2p'|sed -r 's#(^.*et) (.*) (net.*$)#\2#g'

示例文件内容以下:

[root@lzy ~]# vim a.txt
/etc/abc/456
etc

删除文本中的内容,需加转义

[root@Shell ~]# sed -r '\/etc\/abc\/456/d' a.txt

若是碰到/符号, 建议使用#符替换

[root@Shell ~]# sed -r 's#/etc/abc/456#/dev/null#g' a.txt
[root@Shell ~]# sed -r 's@/etc/abc/456@/dev/null@' a.txt 

1.3.17 删除文件

1.3.17.1 删除配置文件中#号开头的注释行, 若是碰到tab或空格是没法删除

[root@Shell ~]# sed '/^#/d' file

1.3.17.2 删除配置文件中含有tab键的注释行

[root@Shell ~]# sed -r '/^[ \t]*#/d' file

1.3.17.3 删除无内容空行

[root@Shell ~]# sed -r '/^[ \t]*$/d' file

1.3.17.4 删除注释行及空行

[root@Shell ~]# sed -r '/^[ \t]*#/d; /^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
[root@Shell ~]# sed -r '/^[ \t]*#|^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
[root@Shell ~]# sed -r '/^[ \t]*($|#)/d' /etc/vsftpd/vsftpd.conf 

1.3.18 给文件行添加注释

1.3.18.1 将第二行到第六行加上注释信息

[root@Shell ~]# sed '2,6s/^/#/' passwd

1.3.18.2 将第二行到第六行最前面添加#注释符

[root@Shell ~]# sed -r '2,6s/.*/#&/' passwd

1.3.19 添加#注释符

[root@Shell ~]# sed -r '3,$ s/^#*/#/' passwd
# sed -r '30,50s/^[ \t]*#*/#/' /etc/nginx.conf 
# sed -r '2,8s/^[ \t#]*/#/' /etc/nginx.conf

1.4 Awk文本处理

awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。

awk数据能够来自标准输入、一个或多个文件,或其它命令的输出。

awk一般是配合脚本进行使用, 是一个强大的文本处理工具。

1.4.1 awk 的处理数据的方式

1.进行逐行扫描文件, 从第一行到最后一行

2.寻找匹配的特定模式的行,在行上进行操做

3.若是没有指定处理动做,则把匹配的行显示到标准输出

4.若是没有指定模式,则全部被操做的行都被处理

1.4.2 awk的语法格式

awk [options] 'commands' filenames

awk [options] -f awk-script-file filenames

1.4.3 选项 options

-F 定义输入字段分隔符,默认的分隔符, 空格或tab键

命令 command

行处理前 行处理 行处理后

BEGIN{} {} END{}

BEGIN发生在读文件以前

[root@Shell ~]# awk 'BEGIN{print 1/2}'
0.5

BEGIN在行处理前, 修改字段分隔符

[root@Shell ~]# awk 'BEGIN{FS=":"} {print $1}' /etc/passwd

BEGIN在行处理前, 修改字段读入和输出分隔符

[root@Shell ~]# awk 'BEGIN{FS=":";OFS="---"} {print $1,$2}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{print 1/2} {print "ok"} END {print "Game Over"}' /etc/hosts
0.5
ok
ok
ok
Game Over

1.4.4 awk命令格式

1.4.4.1 匹配 awk 'pattern' filename

[root@Shell ~]# awk '/root/' /etc/passwd

1.4.4.2 处理动做 awk '{action}' filename

[root@Shell ~]# awk -F: '{print $1}' /etc/passwd

1.4.4.3 匹配+处理动做 awk 'pattern {action}' filename

[root@Shell ~]# awk -F ':' '/root/ {print $1,$3}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{FS=":"} /root/{print $1,$3}' /etc/passwd

1.4.4.4 判断大于多少则输出什么内容 command |awk 'pattern {action}'

[root@Shell ~]# df |awk '/\/$/ {if ($3>50000) print $4}'

1.4.5 Awk工做原理

# awk -F: '{print $1,$3}' /etc/passwd

1.awk将文件中的每一行做为输入, 并将每一行赋给内部变量$0, 以换行符结束

2.awk开始进行字段分解,每一个字段存储在已编号的变量中,从$1开始[默认空格分割]

3.awk默认字段分隔符是由内部FS变量来肯定, 可使用-F修订

4.awk行处理时使用了print函数打印分割后的字段

5.awk在打印后的字段加上空格,由于$1,$3 之间有一个逗号。逗号被映射至OFS内部变量中,称为输出字段分隔符, OFS默认为空格.

6.awk输出以后,将从文件中获取另外一行,并将其存储在$0中,覆盖原来的内容,而后将新的字符串分隔成字段并进行处理。该过程将持续到全部行处理完毕.

1.4.6 Awk内部变量

*1.$0保存当前记录的内容*

[root@Shell ~]# awk '{print $0}' /etc/passwd

*2.NR记录输入总的编号(行号)*

[root@Shell ~]# awk '{print NR,$0}' /etc/passwd
[root@Shell ~]# awk 'NR<=3' /etc/passwd

*3.FNR当前输入文件的编号(行号)*

[root@Shell ~]# awk '{print NR,$0}' /etc/passwd /etc/hosts
[root@Shell ~]# awk '{print FNR,$0}' /etc/passwd /etc/hosts

*4.NF保存行的最后一列*

[root@Shell ~]# awk -F ":" '{print NF,$NF}' /etc/passwd /etc/hosts

5.FS指定字段分割符, 默认是空格

以冒号做为字段分隔符

[root@Shell ~]# awk -F: '/root/{print $1,$3}' /etc/passwd

[root@Shell ~]# awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd

以空格冒号tab做为字段分割

[root@Shell ~]# awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd

6.OFS指定输出字段分隔符*

逗号映射为OFS, 初始状况下OFS变量是空格

[root@Shell ~]# awk -F: '/root/{print $1,$2,$3,$4}' /etc/passwd

[root@Shell ~]# awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2}' /etc/passwd

7.RS输入记录分隔符,默认为换行符[了解]

[root@Shell ~]# awk -F: 'BEGIN{RS=" "} {print $0}' /etc/hosts

8.ORS将文件以空格为分割每一行合并为一行[了解]*

[root@Shell ~]# awk -F: 'BEGIN{ORS=" "} {print $0}' /etc/hosts

*9.print格式化输出函数*

[root@Shell ~]# date|awk '{print $2,"5月份""\n",$NF,"今年"}'
[root@Shell ~]# awk -F: '{print "用户是:" $1 "\t 用户uid: " $3 "\t 用户gid:" $4}' /etc/passwd

printf 函数

[root@Shell ~]# awk -F: '{printf "%-15s %-10s %-15s\n", $1, $2, $3}' /etc/passwd

%s 字符类型

%d 数值类型

占 15 字符

- 表示左对齐,默认是右对齐

printf 默认不会在行尾自动换行,加\n

1.4.7 Awk模式动做

awk语句都由模式和动做组成。

模式部分决定动做语句什么时候触发及触发事件。

若是省略模式部分,动做将时刻保持执行状态。模式能够是条件语句或复合语句或正则表达式。

*1.正则表达式*

匹配记录(整行)

[root@Shell ~]# awk '/^root/' /etc/passwd
[root@Shell ~]# awk '$0 ~ /^root/' /etc/passwd

匹配字段:匹配操做符(~ !~)

[root@Shell ~]# awk '$1~/^root/' /etc/passwd
[root@Shell ~]# awk '$NF !~ /bash$/' /etc/passwd

*2.比较表达式*

比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动做。

比较表达式使用关系运算符,用于比较数字与字符串。

关系运算符

运算符 含义 示例

< 小于 x<y

<= 小于或等于 x<=y

== 等于 x==y

!= 不等于 x!=y

>= 大于等于 x>=y

> 大于 x>y

uid为0的列出来

[root@Shell ~]# awk -F ":" '$3==0' /etc/passwd

uid小于10的所有列出来

[root@Shell ~]# awk -F: '$3 < 10' /etc/passwd

用户登录的shell等于/bin/bash

[root@Shell ~]# awk -F: '$7 == "/bin/bash" ' /etc/passwd

第一列为alice的列出来

[root@Shell ~]# awk -F: '$1 == "alice" ' /etc/passw

为alice的用户列出来

[root@Shell ~]# awk -F: '$1 ~ /alice/ ' /etc/passwd 

[root@Shell ~]# awk -F: '$1 !~ /alice/ ' /etc/passwd

磁盘使用率大于多少则,则打印可用的值

[root@Shell ~]# df |awk '/\/$/'|awk '$3>1000000 {print $4}'

*3.条件表达式*

[root@Shell ~]# awk -F: '$3>300 {print $0}' /etc/passwd
[root@Shell ~]# awk -F: '{if($3>300) print $0}' /etc/passwd
[root@Shell ~]# awk -F: '{if($3>5555){print $3} else {print $1}}' /etc/passwd

*4.运算表达式*

[root@Shell ~]# awk -F: '$3 * 10 > 500000' /etc/passwd
[root@Shell ~]# awk -F: 'BEGIN{OFS="--"} { if($3*10>50000) {print $1,$3} } END {print "打印ok"}' /etc/passwd
[root@Shell ~]# awk '/southem/{print $5 + 10}' datafile 
[root@Shell ~]# awk '/southem/{print $5 + 10.56}' datafile
[root@Shell ~]# awk '/southem/{print $8 - 10}' datafile 
[root@Shell ~]# awk '/southem/{print $8 / 2 }' datafile 
[root@Shell ~]# awk '/southem/{print $8 * 2 }' datafile 
[root@Shell ~]# awk '/southem/{print $8 % 2 }' datafile

*5.逻辑操做符和复合模式*

&&逻辑与 || 逻辑或 !逻辑非

匹配用户名为root而且打印uid小于15的行

[root@Shell ~]# awk -F: '$1~/root/ && $3<=15' /etc/passwd

匹配用户名为root或uid大于5000

[root@Shell ~]# awk -F: '$1~/root/ || $3>=5000' /etc/passwd

1.4.8 awk示例1

# awk '/west/' datafile 
# awk '/^north/' datafile 
# awk '$3 ~ /^north/' datafile 
# awk '/^(no|so)/' datafile 
# awk '{print $3,$2}' datafile
# awk '{print $3 $2}' datafile 
# awk '{print $0}' datafile 
# awk '{print "Number of fields: "NF}' datafile 
# awk '/northeast/{print $3,$2}' datafile
# awk '/^[ns]/{print $1}' datafile 
# awk '$5 ~ /\. [7-9]+/' datafile 
# awk '$2 !~ /E/{print $1,$2}' datafile 
# awk '$3 ~ /^Joel/{print $3 "is a nice boy."}' datafile 
# awk '$8 ~ /[0-9][0-9]$/{print $8}' datafile
# awk '$4 ~ /Chin$/{print "The price is $" $8 "."}' datafile 
# awk '/Tj/{print $0}' datafile 
# awk -F: '{print "Number of fields: "NF}' /etc/passwd 
# awk -F"[ :]" '{print NF}' /etc/passwd 

1.4.9 awk示例2

[root@Shell ~]# cat b.txt 
lzy lizhenya:is a:good boy!
[root@Shell ~]# awk '{print NF}' b.txt
4
[root@Shell ~]# awk -F ':' '{print NF}' b.txt
3
[root@Shell ~]# awk -F"[ :]" '{print NF}' .txt
6

1.4.10 Awk条件判断

if语句格式:{ if(表达式){语句;语句;... }}

打印当前管理员用户名称

[root@Shell ~]# awk -F: '{ if($3==0){print $1 "is adminisitrator"} }' /etc/passwd

统计系统用户数量

[root@Shell ~]# awk -F: '{ if($3>0 && $3<1000){i++}} END {print i}' /etc/passwd

统计普通用户数量

[root@Shell ~]# awk -F: '{ if($3>1000){i++}} END {print i}' /etc/passwd 

if...else 语句格式: {if(表达式){语句;语句;... }else{语句;语句;...}}

# awk -F: '{if($3==0){print $1} else {print $7}}' /etc/passwd
# awk -F: '{if($3==0) {count++} else{i++} }' /etc/passwd
# awk -F: '{if($3==0){count++} else{i++}} END{print " 管理员个数: "count ; print " 系统用户数: "i}' /etc/passwd

if...else if...else 语句格式:

{if(表达式 1){语句;语句;... }else if(表达式 2){语句;语句;. .. }else{语句;语句;... }}

[root@Shell ~]# awk -F: '{ if($3==0){i++} else if($3>0 && $3<1000){j++} else if($3>1000) {k++}} END {print i;print j;print k}' /etc/passwd

[root@Shell ~]# awk -F: '{ if($3==0){i++} else if($3>0 && $3<1000){j++} else if($3>1000) {k++}} END {print "管理员个数"i; print "系统用户个数" j; print "系统用户个 数" }' /etc/passwd
管理员个数1
系统用户个数29
0系统用户个数69

1.4.11 Awk循环语句

1.4.11.1 while循环

[root@Shell ~]# awk 'BEGIN{ i=1; while(i<=10){print i; i++} }'
[root@Shell ~]# awk -F: '{i=1; while(i<=NF){print $i; i++}}' /etc/passwd
[root@Shell ~]# awk -F: '{i=1; while(i<=10) {print $0; i++}}' /etc/passwd
[root@Shell ~]#cat b.txt
111 222
333 444 555
666 777 888 999
[root@Shell ~]# awk '{i=1; while(i<=NF){print $i; i++}}' b.txt

1.4.11.2 for循环

C 风格 for
[root@Shell ~]# awk 'BEGIN{for(i=1;i<=5;i++){print i} }' 
将每行打印 10 次
[root@Shell ~]# awk -F: '{ for(i=1;i<=10;i++) {print $0} }' passwd
[root@Shell ~]# awk -F: '{ for(i=1;i<=NF;i++) {print $i} }' passwd

1.4.12 awk数组概述

将须要统计的某个字段做为数组的索引,而后对索引进行遍历

1.4.12.1 统计/etc/passwd中各类类型shell 的数量*
# awk -F: '{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }' /etc/passwd
1.4.12.2 站访问状态统计<当前时实状态ss>*
[root@Shell ~]# ss -an|awk '/:80/{tcp[$2]++} END {for(i in tcp){print i,tcp[i]}}'
1.4.12.3 统计当前访问的每一个IP的数量<当前时实状态 netstat,ss>*
[root@Shell ~]# ss -an|awk -F ':' '/:80/{ips[$(NF-1)]++} END {for(i in ips){print i,ips[i]}}'

1.4.13 Awk数组案例

Nginx日志分析,日志格式以下:

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
           '$status $body_bytes_sent "$http_referer" '
           '"$http_user_agent" "$http_x_forwarded_for"';
52.55.21.59 - - [25/Jan/2018:14:55:36 +0800] "GET /feed/ HTTP/1.1" 404 162 "https:www.google.com/" "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52" "-"

1.4.13.1 统计当天的PV量

[root@Shell ~]# grep "25/Jan/2018" log.bjstack.log |wc -l

[root@Shell ~]# awk "/25\/Jan\/2018/" log.bjstack.log |wc -l

[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips) {sum+=ips[i]} {print sum}}' log.bjstack.log

1.4.13.2 统计pv量

[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00 {print $0}"' log.bjstack.log |wc -l

1.4.13.3 统计一天内访问最多的10个IP*

[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips){ print ips[i],i}}' log.bjstack.log |sort -rn|head

1.4.13.4 统计访问次数最多的10个IP

[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00"' log.bjstack.log |awk '{ips[$1]++} END {for(i in ips){print ips[i],i}}'|sort -rn|head

1.4.13.5 统计当天访问大于100次的IP*

[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips){if(ips[i]>10){print i,ips[i]}}}' log.bjstack.log

1.4.13.6 统计当天访问最多的10个页面($request top 10)*

[root@Shell ~]# awk '/25\/Jan\/2018/ {request[$7]++} END {for(i in request){print request[i],i}}' log.bjstack.log |sort -rn|head

1.4.13.7 统计当天每一个URL访问内容总大小($body_bytes_sent)*

[root@Shell ~]# awk '/25\/Jan\/2018/ {request[$7]++;size[$7]+=$10} END {for(i in request){print request[i],i,size[i]}}' log.bjstack.log |sort -rn|head

1.4.13.8 统计当天每一个IP访问状态码数量($status)*

[root@Shell ~]# awk '{ip_code[$1 " " $9]++} END {for(i in ip_code){print ip_code[i],i}}' log.bjstack.log|sort -rn|head

1.4.13.9 统计2018年01月25日,访问状态码为404及出现的次数($status)*

[root@Shell ~]# grep "404" log.bjstack.log |wc -l
[root@Shell ~]# awk '{if($9=="404") code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

1.4.13.10 统计2018年01月25日,8:30-9:00访问状态码是404*

[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00" && $9=="404" {code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

[root@Shell ~]# awk '$9=="404" {code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

1.4.13.11 统计2018年01月25日,各类状态码数量,统计状态码出现的次数

[root@Shell ~]# awk '{code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

[root@Shell ~]# awk '{if($9>=100 && $9<200) {i++}

else if ($9>=200 && $9<300) {j++}

else if ($9>=300 && $9<400) {k++}

else if ($9>=400 && $9<500) {n++}

else if($9>=500) {p++}}

END{print i,j,k,n,p,i+j+k+n+p}' log.bjstack.log
相关文章
相关标签/搜索