[shell]-shell

第1章 shell基本概述

1.什么是shell

shell是一个命令解释器,主要用来接收用户的指令,进入驱动操作系统,或硬件。
Linux里有很多种shell,例如:
Bourne Shell(/usr/bin/sh或/bin/sh)
Bourne Again Shell(/bin/bash)
C Shell(/usr/bin/csh)
K Shell(/usr/bin/ksh)
Shell for Root(/sbin/sh)

2.什么是shell脚本

shell脚本就是把命令全部放在一起执行
shell脚本里可以包含若干个变量,循环,if判断,for循环,函数等
特定的格式+特定的语法+系统的命令 = shell脚本

3.shell可以实现什么功能

1.Linux系统支持的命令,都可以用shell实现
2.系统优化脚本,例如:优化SSH 修改端口号 配置yum源 关闭SElinux,时间同步,安装常用软件等操作
3.定时任务,例如每天定时备份数据库的数据
4.日志切割脚本,定时切割日志
5.服务启动脚本,二进制安装的服务没有systemd,可以写脚本启动
6.代码上线脚本,将开发好的代码使用脚本部署到web服务器
7.zabbix自定义监控脚本,使用脚本获取自定义的监控项的数值
8.跳板机脚本,可以使用shell开发一个跳板机

4.学习shell的必备技能

1.熟练的VIM技能
2.熟练的Linux基础命令使用
3.熟练的正则表达式和三剑客命令使用

5.学习shell的正确姿势

1.知道自己要干什么,想要什么效果
2.拿到需求先不要立刻写脚本,先用命令行实现,然后转换成脚本
3.先能看懂,然后模仿,然后会修改,最后能按照自己的需求编写各种shell脚本
4.思考,练习,总结 --> 思考,练习,总结

第2章 shell入门

1.shell书写方式

1.shell脚本名称必须要有含义,切忌随便起名,在公司里容易被打。文件后缀名最好以.sh结尾。
2.shell脚本首行建议添加使用的解释器,如:#!/bin/bash
3.最好给自己的脚本加个注释,注释内容包含了脚本创建时间,作者,以及脚本作用等。
4.注释尽量不要有中文
5.脚本放在专门的目录里

举例:

#!/bin/bash   	#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
# Author: Oldzhang. 526195417@qq.com				 #作者名称
# Create Time 2020/07/31							#创建日期
# Script Description: this is my 1st shell script.	   #脚本描述

2.第一个shell脚本

cat > hello.sh << 
#!/bin/bash
echo "Hello World"
EOF

3.shell执行方式

3.1 执行脚本命令

./test.sh
bash test.sh
source test.sh

3.2 首行不指定解释器

1.如果不在脚本首行指定 #!/bin/bash解释器,那么./执行的时候系统会默认调用bash来执行脚本。
2.那是如果我的脚本是python语言写的,那么执行的使用就会报错。

3.3 首行指定解释器

1.如果首行添加了解释器./执行的时候默认会读取脚本第一行,来确定使用什么解释器运行脚本。

3.4 直接指定解释器运行

我们也可以直接指定使用什么解释器来运行,那样即使脚本首行没有添加解释器也可以运行,例如
bash test.sh
python test.sh

3.5 python的hello

#!/usr/bin/python3
hello = 'hellooooo'
print(hello)

第3章 shell变量

1.什么是变量

变量是Shell传递数据的一种方式。
以一个固定的字符串去表示一个不固定的值,便于后续的复用和维护。

2.变量的分类

环境变量(全局变量)  对整个系统生效
普通变量(局部变量)  只对当前的脚本生效

变量的生存周期
永久的 需要修改环境变量配置文件 变量永久生效 /etc/profile
临时的 直接使用export声明变量即可,关闭shell则变量失效

临时变量的export区别
不加export 则只对当前的shell生效
加export   则对当前打开窗口所有的shell生效

3.环境变量配置文件生效的顺序

1.登陆Shell首先会加载/etc/profile文件
2.然后会执行家目录中的环境变量配置文件
3.按照执行顺序
/etc/profile	
.bash_profile 
.bashrc
/etc/bashrc

4.变量的命名规范

1.以大小写字母 下划线 数字 拼接成变量名,最好以字母开头,最好名字有含义,不然写着写着很容易忘记这个变量干嘛用的
2.变量名=变量值 等号表示给变量赋值,注意等号两边不要有空格
3.系统的环境变量都是大写的,注意不要用系统保留的变量名称,比如PATH
4.变量命名最好不要与系统命令冲突,比如 date=20200731

变量命名参考:

path_data   #全小写
Path_Date   #驼峰写法,首字母大写
PATH_DATA	#全大写

5.变量定义的几种方式

5.1 字符串定义变量

定义一个变量:

[root@m-61 ~]# name="oldya"

查看变量:

[root@m-61 ~]# echo "$name"
oldya
[root@m-61 ~]# echo "${name}"
oldya

5.2 命令定义变量

使用命令定义变量:

[root@m-61 ~]# time=`date`
[root@m-61 ~]# echo ${time}
2020年 07月 31日 星期五 19:11:17 CST
[root@m-61 ~]# time=$(date +%F)
[root@m-61 ~]# echo ${time}    
2020-07-31

命令定义变量两种方式区别:

1.反引号 `命令` 和 $(命令) 都可以表示将命令赋值给变量
2.建议使用$(),因为如果脚本语句里包含单引号和双引号,很容易和反引号搞混,另外也不方便阅读。

5.3 引用变量

引用变量$和 ${}的区别:

[root@m-61 ~]# echo "$name_host"

[root@m-61 ~]# echo "${name}_host"
oldya_host

结论:

如果不加${},可能会造成歧义,使用${}更保险

单引号和双引号区别:

[root@m-61 ~]# echo "name is ${name}" 
name is oldya
[root@m-61 ~]# echo 'name is ${name}'
name is ${name}

结论:

1.单引号不会解析变量,给什么,吐什么
2.双引号可以正确解析变量

什么情况下使用单引号和双引号:

1.如果需要解析变量,就用双引号
2.如果输出的结果要求是普通的字符串,或者需要正确显示特殊字符,则可以用单引号或者撬棍\。

6.变量的传递

6.1 位置参数传递

执行脚本的时候我们可以通过传递参数来实现变量的赋值,接受参数的变量名是shell固定的,我们不能自定义.

脚本如下:

cat > vars.sh <<'EOF'
#!/bin/bash
echo "#当前shell脚本的文件名: $0"
echo "#第1个shell脚本位置参数:$1"
echo "#第2个shell脚本位置参数:$2"
echo "#第3个shell脚本位置参数:$3"
echo "#所有传递的位置参数是: $*"
echo "#所有传递的位置参数是: $@"
echo "#总共传递的参数个数是: $#"
echo "#当前程序运行的 PID 是: $$"
echo "#上一个命令执行的返回结果: $?"
EOF

执行效果:

bash vars.sh 11 22 33 44            
#当前shell脚本的文件名: vars.sh
#第1个shell脚本位置参数:11
#第2个shell脚本位置参数:22
#第3个shell脚本位置参数:33
#所有传递的位置参数是: 11 22 33 44
#所有传递的位置参数是: 11 22 33 44
#总共传递的参数个数是: 4
#当前程序运行的 PID 是: 11943
#上一个命令执行的返回结果: 0

练习题:

1.编写脚本,通过变量传参的形式免交互创建Linux系统用户及密码
2.编写一个通过传参自动修改主机名的脚本

6.2 交互式参数传递

脚本如下:

[root@m-61 ~]# cat read.sh 
#!/bin/bash

#-s 不回显,就是不显示输入的内容
#-n 指定字符个数
#-t 超时时间

read -p "Login: " user
read -s -t20 -p "Passwd: " passwd
echo -e "\n===================="
echo -e "\nlogin: ${user} \npasswd: ${passwd}"

执行效果:

[root@m-61 ~]# bash read.sh 
Login: root
Passwd: 
====================

login: root 
passwd: 123456

练习题:

1.将前面练习的免交互创建用户名密码改写为交互式脚本
2.模拟Linux系统登陆界面
3.编写一个备份脚本,用户传递2个参数,原目录,目标目录
4.编写一个探测主机存活的脚本,交互式的用户输入需要测试的IP地址,然后探测IP地址是否存活
5.编写一个交互式修改主机名和IP地址的脚本
6.编写一个交互式的创建定时任务的脚本,提示用户输入分 时 日 月 周和任务,例如:
*/5 * * * * /sbin/ntpdate time1.aliyun.com > /dev/null 2>&1 

7.变量的运算

7.1 什么是变量运算

顾名思义,变量运算就是对变量的值进行运算,也就是 加 减 乘 除 取余

7.2 变量运算语法

expr 	     #只能做整数运算
$(()) 	   #双括号运算,只支持整数运算,效率高
$[] 	     #整数运算,最简洁
bc,awk 	   #支持小数点
% 		     #取余

7.3 举例

expr

expr 10 + 10
expr 10 - 10
expr 10 * 10
expr 10 \* 10
expr 10 / 10 

num1=10
num2=20
expr ${num1} + ${num2}

$(( ))

echo $((10+10))
echo $((10-10))
echo $((10*10))
echo $((10/10))
echo $((10+10-5))
echo $((10+10-5*6))

num1=10
num2=20
echo $(($num1*$num2))

$[ ]

echo $[10+10]
echo $[10+10*20]
echo $[10+10*20-1000]
echo $[10+10*20/1000]

let

let a=10+10
echo $a

let a=10*10
echo $a    

let a=10/10
echo $a    

let a=$num1+$num2
echo $a

bc

echo 10*10|bc
echo 10*10.5|bc
echo 10-5.5|bc 
echo 10/5.5|bc

awk运算

awk 'BEGIN{print 10+10}'
awk 'BEGIN{print 10-10}'
awk 'BEGIN{print 10*10}'
awk 'BEGIN{print 10/10}'
awk 'BEGIN{print 10^10}'
awk 'BEGIN{print 10-4.5}'
awk 'BEGIN{print 10*4.5}'
awk 'BEGIN{print 10/4.5}'

7.4 练习题

练习题1:根据系统时间打印出今年和明年时间

[root@m-61 ~]# echo "this year is $(date +%Y)"
this year is 2020
[root@m-61 ~]# echo "this year is $(( $(date +%Y) + 1 ))" 
this year is 2021

练习题2:根据系统时间获取今年还剩下多少星期,已经过了多少星期

[root@m-61 ~]# date +%j
214
[root@m-61 ~]# date +%U
30
[root@m-61 ~]# echo "今年已经过了 $(date +%j) days"
今年已经过了 214 days
[root@m-61 ~]# echo "今年还剩 $(( ( 365 - $(date +%j) ) / 7 )) 周"  
今年还剩 21 周
[root@m-61 ~]# echo "今年还剩 $(( ( (365 / 7) - $(date +%U)) )) 周"
今年还剩 22 周

练习题3:完成简单计算功能,通过read方式传入2个值,进行加减乘除

[root@m-61 ~]# cat vars-2.sh 
#!/bin/bash

read -p "请输入要计算的第一个数字: " num1
read -p "请输入要计算的第二个数字: " num2

echo "$num1 + $num2 = $(( $num1 + $num2 ))"
echo "$num1 - $num2 = $(( $num1 - $num2 ))"
echo "$num1 * $num2 = $(( $num1 * $num2 ))"
echo "$num1 / $num2 = $(( $num1 / $num2 ))"

8.作业

概念解释:

1.简单介绍shell脚本是什么,使用场景有哪些?
2.shell脚本的书写规范是什么?
3.shell脚本变量的定义方式有几种?
4.shell脚本如何引用变量?
5.shell脚本特殊变量的意思是什么?$0 $1 $2 $* $@ $# $$ $?
6.变量的运算方式

练习题:

0.今天课上的练习题自己全部写一遍
1.使用Shell脚本打印,系统版本、内核版本平台、主机名、eth0网卡IP地址、lo网卡IP地址、当前主机的外网IP地址,提醒:curl icanhazip.com
2.看谁能用最精简的脚本实现一个计算器
3.查看当前内存使用的百分比和系统已使用磁盘的百分比

拓展:

1.如何创建shell脚本的时候自动把#!/bin/bash和注释内容加上去
2.如何让shell脚本的输出改变颜色
3.思考今天所写的脚本逻辑判断是否严谨

第4章 条件判断

1.基于文件进行判断

1.1 参数说明

2.2 语法

第一种写法

test -f /etc/passwd && echo "true" || echo "false"

第二种写法

[ -f /etc/passwdd ] && echo "true" || echo "false"

2.3 练习

[ -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
[ -e /etc/passwd ] && echo "文件存在" || echo "文件不存在"
[ -r /etc/passwd ] && echo "文件可读" || echo "文件不可读"
[ -w /etc/passwd ] && echo "文件可写" || echo "文件不可写"
[ -x /etc/passwd ] && echo "文件可执行" || echo "文件不可执行"

[ -s /dev/zero ] && echo "true"||echo "false"
[ -s /dev/null ] && echo "true"||echo "false"
[ -s /etc/passwd ] && echo "true"||echo "false"

[ -f /dev/zero ] && echo "true"||echo "false"
[ -f /dev/null ] && echo "true"||echo "false"
[ -f /etc/passwd ] && echo "true"||echo "false"  

2.基于整数进行判断

2.1 参数说明

2.2 练习

单个条件

source /etc/init.d/functions 
[ 1 -eq 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -ne 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -gt 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -lt 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -ge 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -le 2 ] && action OK /bin/true || action NO /bin/false

多个条件

[ 1 -eq 1 -a 2 -gt 1 ] && action OK /bin/true || action NO /bin/false
[ 1 -eq 1 -o 2 -gt 2 ] && action OK /bin/true || action NO /bin/false

3.基于字符串进行判断

3.1 参数说明

3.2 练习

[ 10 == 10 ] && echo "==" || "><"
[ 10 != 5 ] && echo "==" || "><"
name=123
[ -z "$name" ] && echo "true"||echo "false"
[ -n "$name" ] && echo "true"||echo "false"

第5章 shell流程控制之if

1.if单分支

伪代码:

if [ 你是男孩子 ];then
   出门在外要保护好自己
fi

if [ 你是女孩子 ]
then
   无论身时候都不要相信男人说的话
fi

举例:

[root@m-61 ~/scripts]# cat if-1.sh 
#!/bin/bash

if [ "$1" -eq "$2" ]
then
   echo "ok"
fi
[root@m-61 ~/scripts]# bash if-1.sh 2 2
ok
[root@m-61 ~/scripts]# bash if-1.sh 2 4
[root@m-61 ~/scripts]# 

2.双条件分支

伪代码:

if [ 你是男孩子 ]
then
   出门在外要保护好自己
else
   不要相信男人说的话
fi

举例:

if [ "$1" -eq "$2" ]
then
   echo "ok"
else
   echo "error"
fi

3.多条件分支

if [ 你是男孩子 ];then
    出门在外要保护好自己
elif [ 你是女孩子 ];then
    不要相信男人说的话
else 
    你是吃什么长大的
fi

举例:

#!/bin/bash

if [ $1 -eq $2 ];then
   echo "=="
elif [ $1 -gt $2 ];then
   echo ">"
else 
   echo "= or >"
fi

4.练习题

4.0 完善的计算机脚本

#!/bin/bash

#read -p "请输入:" memu 
num1=$1
num2=$2
int=$(echo ${num1}${num2}|sed -r 's#[0-9]+##g')

if [ $# -ne 2 ];then
   echo "请输入2个参数"
   exit
elif [ -z ${int} ];then
   echo "${num1} + ${num2} = $[ ${num1} + ${num2} ]"
   echo "${num1} - ${num2} = $[ ${num1} - ${num2} ]"
   echo "${num1} * ${num2} = $[ ${num1} * ${num2} ]"
   echo "${num1} / ${num2} = $[ ${num1} / ${num2} ]"
else
   echo "请输入2个整数"
fi

4.0 使用IF选择的计算器

需求:

1.使用rede读取用户输入的数字和符号
2.符号使用菜单供用户选择
3.符号使用if作为判断

菜单如下:
请输入第一个数字:10
请输入第二个数字:20
请选择运算符号:
1. +
2. - 
3. *
4. / 
请输入您的选择:1

显示结果:
10 + 20 = 30 

脚本:

#!/bin/bash

read -p "请输入要计算的第一个数字: " num1
read -p "请输入要计算的第二个数字: " num2
echo -e "请选择运算符号:
1. + 
2. - 
3. * 
4. /"

read -p "请输入您的选择: " fuhao

if [ $fuhao == 1 ];then
   echo "$num1 + $num2 = $(( $num1 + $num2 ))"
elif [ $fuhao == 2 ];then
   echo "$num1 - $num2 = $(( $num1 - $num2 ))"
elif [ $fuhao == 3 ];then
  echo "$num1 * $num2 = $(( $num1 * $num2 ))"
elif [ $fuhao == 4 ];then
  echo "$num1 / $num2 = $(( $num1 / $num2 ))"
else 
  echo "请输入1-4"
fi

加入输错判断的脚本:

#!/bin/bash

read -p "请输入要计算的第一个数字: " num1
if [ ! -z $(echo ${num1}|sed -r 's#[0-9]+##g') ];then
  echo "请输入整数"
  exit
fi

read -p "请输入要计算的第二个数字: " num2
if [ ! -z $(echo ${num2}|sed -r 's#[0-9]+##g') ];then
  echo "请输入整数"
  exit
fi

echo -e "请选择运算符号:
1. + 
2. - 
3. * 
4. /"

read -p "请输入您的选择: " fuhao

if [ $fuhao == 1 ];then
   echo "$num1 + $num2 = $(( $num1 + $num2 ))"
elif [ $fuhao == 2 ];then
   echo "$num1 - $num2 = $(( $num1 - $num2 ))"
elif [ $fuhao == 3 ];then
  echo "$num1 * $num2 = $(( $num1 * $num2 ))"
elif [ $fuhao == 4 ];then
  echo "$num1 / $num2 = $(( $num1 / $num2 ))"
else 
  echo "请输入1-4"
fi

4.1 备份文件,如果目录不存在就自动创建

#!/bin/bash

if [ -e /backup/ ];then
   echo "目录已经存在"
else
   mkdir /backup/ -p

4.2 接上一题,判断备份的文件是否存在,如果不存在就提示,然后推出

#!/bin/bash 

if [ -e /backup/ ];then
   echo "目录已经存在"
else
   mkdir /backup/ -p
fi

if [ -f /backup/tar.gz ];then
   echo "文件已经存在"
else
   echo "备份文件中..."
   echo "文件已经创建"
fi

4.3 接上一题,判断备份文件是否为空,如果空就提示,然后推出

if [ -e /backup/ ];then
   echo "目录已经存在"
else
   mkdir /backup/ -p
fi

if [ -f /backup/tar.gz ];then
   echo "文件已经存在"
else
   echo "备份文件中..."
   echo "文件已经创建"
fi

if [ -s /backup/tar.gz ];then
   echo "文件为空"
else
   echo "文件不为空"
fi

4.4 用户执行脚本,传递一个参数作为服务名,检查服务状态。

#!/bin/bash

if [ $# -eq 1 ];then 
    #检查服务的状态
    systemctl status $1 &>/dev/null 
    #判断服务运行的结果
    if [ $? -eq 0 ];then
        echo "$1 服务正在运行" 
    else
        echo "$1 服务没有运行" 
    fi
else
    echo "USAGE: sh $0 service_name"
    exit 
fi

4.5 查看磁盘/当前使用状态,如果使用率超过30%则报警发邮件

梳理思路:

1.查看磁盘分区的状态命令是什么?
2.提取/分区的状态百分比命令是什么?
3.将提取出来的状态百分比和我们设置的阈值进行对比,超过30%报警,不超过就不处理
4.将处理结果写入到文件里

脚本:

#!/bin/bash

#1.提取磁盘使用的百分比
Disk_Status=$(df -h | grep '/$' |awk '{print $5}'|sed 's#%##g')
Time=$(date +%F-%T)

#2.判断磁盘使用百分比是否超过30,如果超过,则写入一个文件中。 
if [ ${Disk_Status} -ge 30 ];then
    echo "${USER}:${Time}: Disk Is Use ${Disk_Status}" >> /tmp/disk_use.txt
fi

4.6 判断用户输入的内容是否为空,如果为空或者直接按回车,则提醒,否则输出用户输入的内容。

#!/bin/bash

read -p "请输入内容: " word

if [ -z ${word} ];then
    echo "输入的内容为空."
else
    echo "输入的内容为:${word}" 
fi

4.7 编写一个用来检查用户的uid和gid是否一致的脚本

1.使用交互式接收用户输入的用户名作为参数
2.如果用户不存在,就输出提醒然后退出脚本
3.如果用户存在,判断这个用户的uid和gid是否一致
4.如果uid和gid一致,则输出正确信息并打印出用户的uid和gid,如果不一致则输出实际的uid和gid

思路:

1.判断用户是否存在的命令是什么?
2.提取uid和gid的命令是什么?
3.对比uid和gid的命令是什么?

第一种写法:思考,这样写有没有问题

#!/bin/bash

USER=$1
USER_OK=$(grep -w "^${User}" /etc/passwd|wc -l)
UID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $3}' /etc/passwd)
GID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $4}' /etc/passwd)

if [ "${USER_OK}" -eq 1 ] && [ "${UID}" -eq "${GID}" ];then
    echo "用户uid与gid一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
elif [ "${USER_OK}" -eq 1 -a "${UID}" ! -eq "${GID}" ];then
    echo "用户uid与gid不一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
else
    echo "查询的用户不存在" 
fi

完善的判断脚本:

#!/bin/bash

USER=$1
USER_Ok=$(grep -w "^${User}" /etc/passwd|wc -l)
UID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $3}' /etc/passwd)
GID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $4}' /etc/passwd)

#1.判断是否存在这个用户
if [ "${USER_Ok}" -eq 0 ];then
    echo "查询的用户用户不存在"
    exit
elif [ "${UID}" -eq "${GID}" ];then
    echo "用户uid与gid一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
else
    echo "用户uid与gid不一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
fi

难点:

1.awk如何使用变量
2.如果用户名字符串有重复的内容如何精确定位
3.判断的逻辑如何精简

4.8 成绩查询

提醒用户输入自己的成绩
1.如果分数大于0小于59则提示需要补考
2.如果分数大于60小于85则提示成绩良好
3.如果分数大于86小于100提示成绩优秀

脚本:

#!/bin/bash

read -p "来查成绩吧:" score

if [ ${score} -ge 0 ] && [ ${score} -le 59 ];then
    echo "补考吧兄弟"
elif [ ${score} -ge 59 ] && [ ${score} -le 85 ];then
    echo "这次饶你一命"
elif [ ${score} -ge 86 ] && [ ${score} -le 100 ];then
    echo "这么厉害,你是吃什么长大的"
else 
    echo "查询范围是0-100哦"
    exit
fi

思考:这个脚本存在的缺陷

1.如果用户输入了多个参数或者没有输入参数呢
2.如果用户输入的不是说字而是字符串呢

完善之后的脚本:

#!/bin/bash

if [ $# != 0 ];then
    echo "请不要带参数查询"
    exit
else 
    read -p "来查成绩吧:" score
    if_num=$(echo "${score}"|sed -r 's#[0-9]+##g')
    
    if [ -n "${if_num}" ];then
        echo "请输入整数"
        exit 
    elif [ ${score} -ge 0 ] && [ ${score} -le 59 ];then
        echo "补考吧兄弟"
    elif [ ${score} -ge 59 ] && [ ${score} -le 85 ];then
        echo "这次饶你一命"
    elif [ ${score} -ge 86 ] && [ ${score} -le 100 ];then
        echo "这么厉害,你是吃什么长大的"
    else 
        echo "查询范围是0-100哦"
        exit
    fi
fi

4.9 判断输入的数字是否为整数方法

#!/bin/bash

input=$1 

#第一种方法
expr ${input} + 1 > /dev/null 2>&1
if [ $? != 0 ];then
    echo "请输入整数"
fi

#第二种方法
num=$(echo ${input}|sed -r 's#^[0-9]+##g')
if [ -n "${num}" ];then
    echo "请输入整数"
fi

#第三种方法
if [[ ! "${input}" =~ ^[0-9]+$ ]];then 
    echo "请输入纯数字"
fi

4.10 查询nginx服务状态

深圳5期-夏燕同学提供

#!/bin/bash

read -p "请输入您要查询的服务名称:" SEname
rpm -qa|grep ${SEname} > /dev/null  2>&1
if [ $? -eq 0 ];then
  read -p "请选择这行的步骤:
1.start
2.stop
3.restart
4.status
请输入您的选择:" SEst
else
  echo "您要查询的服务没有安装"
  exit
fi

if [ ${SEst} -eq 1 ];then
  systemctl start ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 启动成功"
  else
    echo "${SEname} 启动失败"
  fi
elif [ ${SEst} -eq 2 ];then
  systemctl stop ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 已经停止"
  else
    echo "${SEname} 停止失败"
  fi
elif [ ${SEst} -eq 3 ];then
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 重启成功"
  else
    echo "${SEname} 重启失败"
  fi
elif [ ${SEst} -eq 4 ];then
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 正在查询状态"
  else
    echo "${SEname} 查询失败"
  fi
else
  echo "请重新输入您的选择,只能输入1-4"
fi

深圳5期-纪城

#!/bin/sh

source /etc/init.d/functions
read -p "请输入你要查询的服务名称: " MING
  rpm -qa $MING >/tmp/1.txt
  if [ ! -s /tmp/1.txt ];then
    echo "输入的服务不存在";exit
  fi
echo -e "请选择执行的步骤: \n1.start\n2.stop\n3.restart\n4.status"
read -p "请输入你的选择: " XUAN
if [ $XUAN -eq 1 ];then
  systemctl start $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 启动成功" /bin/true || action "$MING 启动失败" /bin/false
elif [ $XUAN -eq 2 ];then
  systemctl stop $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 关闭成功" /bin/true || action "$MING 关闭失败" /bin/false
elif [ $XUAN -eq 3 ];then
  systemctl restart $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 重启成功" /bin/true || action "$MING 重启失败" /bin/false
elif [ $XUAN -eq 4 ];then
  systemctl status $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 服务开启" /bin/true || action "$MING 服务关闭" /bin/false
else
  echo "请重新输入正确的选择项"
fi

小张白嫖版

#!/bin/bash

source /etc/init.d/functions
read -p "请输入您要查询的服务名称:" SEname
rpm -ql ${SEname} > /dev/null  2>&1
if [ $? -eq 0 ];then
  read -p "请选择这行的步骤:
1.start
2.stop
3.restart
4.status
请输入您的选择:" SEst
else
  echo "您要查询的服务没有安装"
  exit
fi

if [ ${SEst} -eq 1 ];then
  echo "${SEname} 启动中..."
  sleep 1
  systemctl start ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    action "${SEname} 启动成功" /bin/true
  else
    action "${SEname} 启动失败" /bin/false
  fi
elif [ ${SEst} -eq 2 ];then
  echo "${SEname} 停止中..."
  sleep 1
  systemctl stop ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    action "${SEname} 已经停止" /bin/true
  else
    action "${SEname} 停止失败" /bin/false
  fi
elif [ ${SEst} -eq 3 ];then
  echo "${SEname} 重启中..."
  sleep 1
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    action "${SEname} 重启成功" /bin/true
  else
    action "${SEname} 重启失败" /bin/false
  fi
elif [ ${SEst} -eq 4 ];then
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 正在查询状态"
  else
    action "${SEname} 查询失败" /bin/false
  fi
else
  echo "请重新输入您的选择,只能输入1-4"
fi

5.作业

1.猜数字
2.多极菜单
3.根据选择安装不同软件
4.编写服务启动脚本
5.编写系统优化脚本
- 根据系统版本选择对应的YUM源
- 关闭防火墙和selinux
- 将时间同步写入定时任务
- 安装常用软件
- 修改主机名和IP地址
6.日志切割脚本

第4章 shell流程控制之case

1.case介绍

case和if都是用来处理多分支判断的,只不过case更加简洁和规范一些。

2.case使用场景

我们可以根据用户输入的参数来进行匹配,不同的匹配选项执行不同的操作步骤。
比如:服务的启动脚本 {start|restart|stop} 等操作

3.case基本语法

case $1 in. 
start)
  command
  ;;
restart)
  command
  ;;
stop)
  command
  ;;
*)
  command
esac  

4.if和case的区别

下面我们以一个小例子来说明if和case的区别

4.1 需求

根据用户选择的序号执行相应的操作

4.2 if的写法

#!/bin/bash

echo -e "================
1.取钱
2.存钱
3.还剩多少
================"

read -p "请输入你要执行的操作: " num

if [ "${num}" -eq 1 ];then
    echo "取好了"
elif [ "${num}" -eq 2 ];then
    echo "存好了"
elif [ "${num}" -eq 3 ];then 
    echo "还剩-100元"
else
    echo "输入有误,下次走点心"
fi

4.3 case的写法

#!/bin/bash

echo -e "================
1.取钱
2.存钱
3.还剩多少
================"

read -p "请输入你要执行的操作: " num

case ${num} in 
    1)
        echo "取好了"
        ;;
    2)
        echo "存钱"
        ;;
    3)
        echo "还剩-100元"
        ;;
    *)
        echo "其输入正确的数据"
esac

5.综合练习题

5.1 使用case编写友好输出的计算器

需求:

1.交互式接受用户输入的数字和计算方法
3.判断用户输入的参数是否为3个
4.判断用户输入的是否为整数数字
5.判断用户输入的符号是否为+-*%
6.如果用户输入错误则友好提醒正确的使用方法

脚本:

#!/bin/bash

read -p "请输入要计算的第一个数字: " num1
if [ ! -z $(echo ${num1}|sed -r 's#[0-9]+##g') ];then
  echo "请输入整数"
  exit
fi

read -p "请输入要计算的第二个数字: " num2
if [ ! -z $(echo ${num2}|sed -r 's#[0-9]+##g') ];then
  echo "请输入整数"
  exit
fi

echo -e "请选择运算符号:
1. + 
2. - 
3. * 
4. /"

read -p "请输入您的选择: " fuhao

case ${fuhao} in
    1)
        echo "$num1 + $num2 = $(( $num1 + $num2 ))"
        ;;
    2)
        echo "$num1 - $num2 = $(( $num1 - $num2 ))"
        ;;
    3)
        echo "$num1 * $num2 = $(( $num1 * $num2 ))"
        ;;
    4)
        echo "$num1 / $num2 = $(( $num1 / $num2 ))"
        ;;
    *)
        echo "请输入1-4"
esac

5.2 编写非交互的服务启动脚本

需求:

1.使用case编写非交互的服务管理脚本
2.如果用户输入参数错误,则友好提醒脚本的使用方法

脚本:

#!/bin/bash

source /etc/init.d/functions

SERVICE=$1
case ${SERVICE} in
  start)
      echo "nginx 启动中..."
      sleep 1
      nginx
      if [ $? -eq 0 ];then
        action "nginx 启动成功" /bin/true
      else
        action "nginx 启动失败" /bin/false
      fi
      ;;
  stop)
      echo "nginx 停止中..."
      sleep 1
      nginx -s stop
      if [ $? -eq 0 ];then
        action "nginx 已经停止" /bin/true
      else
        action "nginx 停止失败" /bin/false
      fi
      ;;
  restart)
      echo "nginx 重启中..."
      nginx -s stop
      sleep 1
      nginx
      if [ $? -eq 0 ];then
        action "nginx 重启成功" /bin/true
      else
        action "nginx 重启失败" /bin/false
      fi
      ;;
  reload)
      nginx -s reload
      if [ $? -eq 0 ];then
        action "nginx 正在重新载入" /bin/true
      else
        action "nginx 重新载入失败" /bin/false
      fi
      ;;
  check)
      nginx -t 
      ;;
  *) 
      echo "Usage: {start|stop|restart|reload|check}"
esac

5.3 模拟用户登陆

需求:

1.提前创建一个用户记录文件,格式为
用户名:密码
2.用户执行脚本打印菜单
- 登陆
- 注册
3.登陆菜单的选项
- 请输入账号名
- 请输入密码
- 如果账号密码正确,则登陆成功
4.注册
- 请输入用户名,然后检查用户名是否已经存在
- 请输入密码
- 请再次输入密码
- 将用户名和密码写入文本里,如果成功则返回注册成功的结果
- 返回登陆页面

脚本:

#!/bin/bash

name_list=bank.txt
log=log.txt
time=$(date)

echo "
===========
1.登陆
2.注册
===========
"
read -p "请选择需要的操作:" memu 

case ${memu} in 
  1)
      read -p "请输入用户名:" name
      grep -wo "${name}" ${name_list} >> /dev/null 2>&1
      if [ $? != 0 ];then
         echo "用户名不存在,请重新输入"
         exit
      fi

      read -p "请输入密码:" passwd_input
      passwd_user=$(awk -F":" "/^${name}\:/"'{print $2}' ${name_list})
      #passwd_user=$(sed -rn "s#${name}:(.*)#\1#g"p bank.txt)
      if [ ${passwd_input} == ${passwd_user} ];then
         echo "登陆成功!"
         echo "${time} ${name} 登陆成功!" >> ${log}
      else 
         echo "密码错误,请重新输入"
         echo "${time} ${name} 登陆失败!" >> ${log}
         exit
      fi
      ;;

  2)
      read -p "请输入注册用户名:" name
      grep -wo "${name}" ${name_list} >> /dev/null 2>&1
      if [ $? = 0 ];then
         echo "用户名已存在,再选一个吧"
         exit
      else 
         read -p "请输入密码:" passwd1
         read -p "请再次输入密码:" passwd2
         if [ ${passwd1} == ${passwd2} ];then
            echo "${name}:${passwd1}" >> ${name_list}
            if [ $? == 0 ];then
               echo "注册成功,请登录"
               echo "${time} ${name} 注册成功!" >> ${log}
            else 
               echo "注册失败,请联系管理员"
               echo "${time} ${name} 注册失败!" >> ${log}
            fi
         else
            echo "两次输入的密码不一致,请重新输入"
            exit
         fi
      fi
      ;;

  *)
      echo "请选择1-2的数字"
esac

5.4 模拟用户银行取钱

需求:

1.提前创建一个用户记录文件,格式为
用户名:密码:存款数
2.用户执行脚本提醒输入账号密码
- 如果用户不存在则提醒输入正确用户
- 如果用户密码不对则提醒输入正确账号密码
3.如果账号和密码都输入正确则打印功能菜单
- 查询余额
- 存钱
- 取钱
- 退出
4.限定条件
- 存钱必须是正整数,不能是负数,小数点或字母
- 取钱必须是正整数,不能是负数,小数点或字母
- 取钱不能超过余额总数
5.根据用户选择的菜单功能进行余额的更新
6.将用户操作的信息和时间都打印到日志里
7.管理员功能
- 修改金额
- 修改密码
- 黑名单
- 白名单
- 金蝉脱壳

脚本:

#!/bin/bash

BK=bank_info.txt
LOG=bank_log.txt
TIME=$(date)
BK_BL=bank_black.txt
SS=~/.ssh/id_rsa

if [ ! -f ${BK} ];then
    touch ${BK}
    touch ${BK_BL}
    echo "${TIME} 创建${BK} ${BK_BL}成功" >> ${LOG}
fi

echo "
欢迎来到天堂银行:
1)登录
2)注册
"
read -p "请输入您的选择:" menu
case $menu in 
    1)
        #用户输入账号
        read -p "请输入您的账户:" input_name 

        #判断用户是否被拉黑
        name=$(grep "^${input_name}:" ${BK_BL}|wc -l)        
        if [ ${name} -ne 0 ];then
           echo "您已被列入黑名单!"
           exit
        fi
 
        #判断用户是否存在
        name=$(grep "^${input_name}:" ${BK}|wc -l)        
        if [ ${name} -eq 0 ];then
           echo "登录用户不存在"
           exit
        else 
            #用户输入密码
            read -p "请输入您的密码:" input_pass 

            #判断密码是否正确
            pass=$(awk -F ":" '/^'"${input_name}"':/{print $2}' ${BK} )
            if [ ${input_pass} == ${pass} ];then
               echo "${TIME} ${input_name} 登录成功!" >> ${LOG}
               echo "登录成功!"
            else
               echo "${TIME} ${input_name} 登录失败!" >> ${LOG}
               echo "密码错误!"
               exit
            fi
        fi
 
        #用户菜单        
        echo " 
        1)查询
        2)存钱
        3)取钱
        " 
        read -p "请输入您的选择:" menu 
 
        case $menu in 
            1)
                #查询
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                echo "您的余额为: ${money}亿元"
		;;
            2)
                #判断输入是否为整数
                read -p "请输入您要存的金额: " input_money
                money_int=$(echo "${input_money}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${money_int} ];then
                   echo "请输入整数金额"
                   exit
                fi
                
                #存钱操作
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                new_money=$[ ${money} + ${input_money} ]
                sed -ri "/^${input_name}:/s#(.*):(.*)#\1:${new_money}#g" ${BK}
                if [ $? == 0 ];then
                   echo "${TIME} ${input_name} 存入成功,最新余额为: ${new_money}" >> ${LOG}
                   echo "存入成功,最新余额为: ${new_money}"
                else
                   echo "${TIME} ${input_name} 存入失败,最新余额为: ${new_money}" >> ${LOG}
                   echo "存钱失败,请联系管理员"
                fi
		;;
            3)
                #输出余额
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                echo "最新余额为: ${money}"
                read -p "请输入您取走的金额: " input_money
                
                #判断输入是否为整数
                money_int=$(echo "${input_money}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${money_int} ];then
                   echo "请输入整数金额"
                   exit
                fi

                #判断取钱是否超过余额
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                if [ ${input_money} -gt ${money} ];then
                    echo "${TIME} ${input_name} 取钱失败,余额不足: ${money}" >> ${LOG}
                    echo "取钱失败,余额不足"
                    exit
                fi
           
                #执行取钱操作
                new_money=$[ ${money} - ${input_money} ]
                sed -ri "/^${input_name}:/s#(.*):(.*)#\1:${new_money}#g" ${BK}
 
                #判断是否取钱成功
                if [ $? == 0 ];then
                   echo "${TIME} ${input_name} 取钱成功,最新余额为: ${new_money}" >> ${LOG}
                   echo "取钱成功,最新余额为: ${new_money}"
                else
                   echo "${TIME} ${input_name} 取钱失败,最新余额为: ${new_money}" >> ${LOG}
                   echo "取钱失败,请联系管理员"
                fi
		;;
            *)
                echo "1-3"
        esac 
        ;;
    2)
        read -p "请输入您的账户:" input_name
        
        #判断用户是否被拉黑
        name=$(grep "^${input_name}:" ${BK_BL}|wc -l)        
        if [ ${name} -ne 0 ];then
           echo "您已被列入黑名单!"
           exit
        fi

        #判断用户是否存在
        name=$(grep "^${input_name}:" ${BK}|wc -l)        
        if [ ${name} -ne 0 ];then
           echo "用户已存在,换个吧!"
           exit
        fi
        
        #判断密码是否有非法字符和整数
        read -p "请输入您的密码:" input_pass1 
        read -p "请输入您的密码:" input_pass2
        if [ $input_pass1 == $input_pass2 ];then
            pass=$(echo "${input_pass1}"|sed -r 's#[0-9]+##g')
            if [ ! -z ${pass} ];then
               echo "请输入整数密码"
               exit
            fi 
        else
            echo "两次输入的密码不一致"
            exit
        fi

        #判断金额是否为整数
        read -p "请存钱:" input_money
        money_int=$(echo "${input_money}"|sed -r 's#[0-9]+##g')
        if [ ! -z ${money_int} ];then
           echo "请输入整数金额"
           exit
        fi

        echo "${input_name}:${input_pass1}:${input_money}" >> ${BK}
        echo "${TIME} ${input_name} 注册成功,最新余额为:${input_money}" >> ${LOG}
        echo "注册成功!请登陆!"
        ;;
    admin)
        echo "${TIME} admin 登录成功" >> ${LOG} 
        echo "
        1)修改余额
        2)修改密码
        3)黑名单
        4)白名单
        5)金蝉脱壳
        "
        read -p "请选择:" menu 
        case $menu in 
            1) 
                echo "当前用户余额信息:"
                echo "$(awk -F":" '{print $1":"$3}' ${BK})" 

                #用户输入账号
                read -p "请输入您的账户:" input_name 
 
                #判断用户是否存在
                name=$(grep "^${input_name}:" ${BK}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "登录用户不存在"
                    exit
                fi

                #判断输入是否为整数
                read -p "请输入要修改的整数金额:" input_money1
                money_int=$(echo "${input_money1}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${money_int} ];then
                   echo "请输入整数金额"
                   exit
                else
                   read -p "请输入要修改的整数金额:" input_money2
                   if [ $input_money1 != $input_money2 ];then
                       echo "两次输入的金额不一致"
                       exit
                   fi
                fi
              
                #修改金额
                sed -ri "/^${input_name}:/s#(.*):(.*)#\1:${input_money1}#g" ${BK}

                #判断是否取钱成功
                if [ $? == 0 ];then
                   echo "${TIME} admin 修改 ${input_name} 余额成功,最新余额为: ${input_money1}" >> ${LOG}
                   echo "修改余额成功,最新余额为: ${input_money1}"
                else
                   echo "${TIME} admin 修改 ${input_name} 余额失败,最新余额为: ${input_money1}" >> ${LOG}
                   echo "修改余额失败"
                fi
                ;;
            2)
                echo "当前用户信息:"
                echo "$(awk -F":" '{print $1}' ${BK})" 

                #用户输入账号
                read -p "请输入您的账户:" input_name 
 
                #判断用户是否存在
                name=$(grep "^${input_name}:" ${BK}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "登录用户不存在"
                    exit
                fi
                
                #判断输入是否为整数
                read -p "请输入要修改的整数密码:" input_pass1
                pass_int=$(echo "${input_pass1}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${pass_int} ];then
                   echo "请输入整数"
                   exit
                else
                   read -p "请输入要修改的整数密码:" input_pass2
                   if [ $input_pass1 != $input_pass2 ];then
                       echo "两次输入的密码不一致"
                       exit
                   fi
                fi
              
                #修改密码
                sed -ri "/^${input_name}:/s#(.*):(.*):(.*)#\1:${input_pass1}:\3#g" ${BK}

                #判断是否取钱成功
                if [ $? == 0 ];then
                   echo "${TIME} admin 修改 ${input_name} 密码成功" >> ${LOG}
                   echo "修改密码成功"
                else
                   echo "${TIME} admin 修改 ${input_name} 密码失败" >> ${LOG}
                   echo "修改密码失败"
                fi
                ;;
            3)
                echo "当前用户信息:"
                echo "$(awk -F":" '{print $1}' ${BK})" 

                #用户输入账号
                read -p "请输入您的账户:" input_name 
 
                #判断用户是否存在
                name=$(grep "^${input_name}:" ${BK}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "登录用户不存在"
                    exit
                fi
                 
                #将用户信息加入黑名单
                grep "^${input_name}:" ${BK} >> ${BK_BL}
                if [ $? == 0 ];then
                    sed -i "/^${input_name}:/d" ${BK}
                    echo "${TIME} admin 将${input_name} 用户添加黑名单成功" >> ${LOG}
                    echo "用户添加黑名单成功"
                    echo "当前最新黑名单记录为:"
                    awk -F":" '{print $1}' ${BK_BL}
                else
                    echo "${TIME} admin 将${input_name} 用户添加黑名单失败" >> ${LOG}
                    echo "用户添加黑名单失败"
                fi 
                ;;
            4)
                echo "当前黑名单用户信息:"
                echo "$(awk -F":" '{print $1}' ${BK_BL})" 

                #用户输入账号
                read -p "请输入您的账户:" input_name 
 
                #判断用户是否存在
                name=$(grep "^${input_name}:" ${BK_BL}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "用户不存在"
                    exit
                fi
                 
                #将用户信息加入白名单
                grep "^${input_name}:" ${BK_BL} >> ${BK}
                if [ $? == 0 ];then
                    sed -i "/^${input_name}:/d" ${BK_BL}
                    echo "${TIME} admin 将${input_name} 用户添加白名单成功" >> ${LOG}
                    echo "用户添加白名单成功"
                    echo "当前最新黑名单记录为:"
                    awk -F":" '{print $1}' ${BK_BL}
                    echo "当前最新白名单记录为:"
                    awk -F":" '{print $1}' ${BK}
                else
                    echo "${TIME} admin 将${input_name} 用户添加白名单失败" >> ${LOG}
                    echo "用户添加白名单失败"
                fi 
                ;;
            5)
                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat $0|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv $0 /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}

                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat ${BK}|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv ${BK} /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}

                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat ${BK_BL}|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv ${BK_BL} /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}

                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat ${LOG}|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv ${LOG} /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}
                ;;
            *)     
        esac
        ;;
    *)
        echo "{1|2}"
esac

5.5 日志分析脚本

需求:

1.按要求分析nginx日志
2.打印出功能菜单
-- 查询PV
-- 查询最高IP
-- 查询访问最多的URL
-- 查询每个爬虫各访问了多少次

脚本:

#!/bin/bash 

#1.显示服务信息
echo "==============================
服务器名:$(hostname)
服务器IP:$(hostname -I)
查询日志为:xxx.com_access.log
查询时间为: $(date +%F)
=============================="
#2.PV数
echo "PV数量为: $(wc -l bbs.xxxx.com_access.log|awk '{print $1}')"
echo "=============================="
#3.搜索引擎次数
echo "搜索情况汇总"
echo "搜索引擎总计访问次数: $(egrep -i 'bot|spider|Spider' bbs.xxxx.com_access.log |wc -l)"
echo "Baidu访问次数:      $(egrep -i 'Baiduspider' bbs.xxxx.com_access.log |wc -l)"
echo "bing访问次数:       $(egrep -i 'bingbot' bbs.xxxx.com_access.log |wc -l)"
echo "Google访问次数:     $(egrep -i 'googlebot' bbs.xxxx.com_access.log |wc -l)"
echo "sougou访问次数:     $(egrep -i 'Sogou web spider|pic.sogou.com' bbs.xxxx.com_access.log |wc -l)"
echo "yisou访问次数:      $(egrep -i 'YisouSpider' bbs.xxxx.com_access.log |wc -l)"
echo "brandwatch访问次数: $(egrep -i 'brandwatch' bbs.xxxx.com_access.log |wc -l)"
#4.TOP IP
echo "=============================="
echo "访问最多IP前10为:"
num=1
exec < ip.txt
while read line 
do
   num=`echo ${line}|awk '{print $1}'`
   ip=`echo ${line}|awk '{print $2}'`
   host=`curl -s cip.cc/${ip}|awk '/地址/{print $3}'`
   echo "${num} ${ip} ${host}" 
   sleep 2
done

#5.其他
echo "=============================="
echo "监控关键链接为:GET /thread-"
echo "=============================="
echo "关键链接PV访问次数: $(grep "GET /thread-" bbs.xxxx.com_access.log|wc -l)"
echo "=============================="
echo "关键链接平均响应时间为: $(grep "GET /thread-" bbs.xxxx.com_access.log|awk '{sum+=$NF} END {print  sum/NR}')"
echo "=============================="
echo "关键链接访问响应时间排名"
echo "$(awk '{print $NF}' bbs.xxxx.com_access.log |grep -v "-"|cut -b -3|sort|uniq -c|sort -nr|head -10)"

5.6 代码分发脚本

需求:

1.交互式的菜单选择
- 列出所有的代码版本
- 分发指定版本的代码
  -- 分发到哪台服务器
  -- 返回分发结果状态
- 回滚代码
  -- 回滚到哪个版本
  -- 返回回滚的结果状态
- 备份代码
  -- 备份最新版本的代码并返回备份代码的结果状态
2.如果用户输入错误,则友好提醒

脚本:


5.7 自动封锁高频IP

需求:

1.从Nginx日志里提取5分钟内访问频次最高的IP
2.如果这个IP地址1分钟访问超过了100次那么就列入黑名单(可以使用防火墙也可以使用nginx的黑名单)
3.如果这个IP已经在黑名单里就不在重复添加
4.每个IP只封锁1分钟然后就可以恢复访问(从黑名单删除)
4.所有的操作都打印信息存储到日志里

脚本:


5.8 阅读并加注释别人写的脚本

需求:

1.理解这个脚本是做什么的
2.每一行添加注释

脚本内容:

#!/bin/bash

. /etc/init.d/functions

conut=10 
Path=/server/scripts/access.log
function ipt(){ 
    awk  '{print $1}'$Path|sort|uniq -c|sort -rn >/tmp/tmp.log
    exec < /tmp/tmp.log
    while read line
    do
        ip=echo $line|awk '{print $2}'
        if [ echo $line|awk '{print $1}' -ge $conut -a iptables -L -n|grep "$ip"|wc -l -lt 1 ]
        then
            iptables -I INPUT -s $ip -j DROP
            RETVAL=$?
            if [ $RETVAL -eq 0 ]
            then
                action "iptables -I INPUT -s $ip -j DROP" /bin/true
                echo "$ip" >>/tmp/ip_$(date +%F).log
            else
                action "iptables -I INPUT -s $ip -j DROP" /bin/false
            fi
        fi
    done
}

function del(){
[ -f /tmp/ip_$(date +%F -d '-1 day').log ]||{
    echo "log is not exist"
    exit 1} 
    exec </tmp/ip_$(date +%F -d '-1 day').log
    while read line
    do
        if [ iptables -L -n|grep "$line"|wc -l -ge 1 ]
        then
            iptables -D INPUT -s $line -j DROP
        fi
    done
}

function main(){
    flag=0
    while true
    do
        sleep 180
        ((falg++))
        ipt
        [ $flag -ge 480 ] && del && flag=0
    done
}
main

第5章 shell流程控制之for循环

1.for循环使用场景

1.一次处理不完的任务或者需要重复执行多次的任务
2.比如创建生成100个用户,创建100个文件,批量判断文件是否存在等

2.for循环基本语法

for 变量名 in  取值列表
do
    每次循环要执行的内容
done

3.for循环的几种方法

3.1 手动给定多个字符串

举例1:

#!/bin/bash

for name in name1 name2 name3
do
   echo ${name}
done

举例2: 提问,请问输出的是几个值

#!/bin/bash

for name in name1 "name2 name3" name4 "name5 name6" 
do
   echo ${name}
done

3.2 从变量里取值

#!/bin/bash

name_list="name1 name2 name3"

for name in ${name_list}
do
  echo "${name}"
done

3.3 从文件里取值

#!/bin/bash

for name in $(cat /etc/passwd)
do
    echo "${name}"|awk -F":" '{print $1}'
done

3.4 生成数字序列

方法1:

#!/bin/bash

for num in {1..100}
do 
    echo ${num}
done

方法2:

#!/bin/bash

for  (( num=0;num<10;num++ ))
do 
    echo ${num}
done

4.练习题

批量检测网段里存活的主机

需求:

检查10.0.0.1 - 10.0.0.100范围内存活的主机

脚本:


批量创建用户和密码

需求:

1.批量创建 user1admin - user10admin 个用户
2.密码为admin1user - admin10user

批量创建用户和密码V2

需求:

用户: user1 - user10
密码: passwd10 - passwd1

从指定的文本里给出的用户名密码批量创建用户

需求:


接上题,加上用户的uid和gid


接上题,使用case提供添加和删除和查询功能

case菜单
useradd
userdel
check

获取本机所有的用户并按序号排列

需求:

user1:root
user2:oldya
user3:oldzhang

嵌套循环

需求:

有2个文本,一个IP文本,一个端口文本
IP文本里存放存活主机
端口文本存放需要检查的端口
然后探测每个主机开放了哪些端口

参考命令

ping -c1 $ip &>/dev/null
nc -z -w 1 $ip $port &>/dev/null

脚本:


对比两个文本不同的内容

需求:

1.有两个用户名的文本user1.txt和user2.txt
2.对比user1的用户名是否存在与user2里

脚本:


mysql分库分表备份

需求:

1.数据库备份在/backup/mysql目录
2.按照"/backup/mysql/日期—IP/库名/表名"的格式备份数据

脚本:

#!/bin/bash

DB_LIST=$(mysql -uroot -p123456 -e "show databases;"|egrep -v "Database|_schema")
BACKUP=/backup/mysql

for DB_NAME in $DB_LIST
do
    TABLE_LIST=$(mysql -uroot -p123456 -e "show tables from ${DB_NAME}"|grep -v 'Tables')
    for TABLE_NAME in ${TABLE_LIST}
    do
        mkdir -p ${BACKUP}/${DB_NAME}   
        mysqldump --single-transaction -uroot -p123456 ${DB_NAME} ${TABLE_NAME} >> ${BACKUP}/${DB_NAME}/${TABLE_NAME}.sql
    done
done

抓取blog的文章标题

目标博客:

https://www.cnblogs.com/alaska/default.html?page=1

需求:

1.批量获取博客的所有文章链接
2.过滤和整理出所有的文章标题

脚本:

#!/bin/bash

curl -s https://www.cnblogs.com/alaska/default.html?page=1 -o blog.html

PAGE_MAX=$(grep "page=" blog.html|sed -r 's#.*page=(.*)".*$#\1#g'|sort|tail -1)
echo "一共有${PAGE_MAX}"

for line in $(seq 1 ${PAGE_MAX})
do
    curl -s https://www.cnblogs.com/alaska/default.html?page=${line} -o page_${line}.html
    for num in $(awk '/postTitle2/{print NR}' page_${line}.html)
    do
        url=$(awk -F "\"" 'NR=='${num}'{print $4}' page_${line}.html ) 
        title_line=$[ ${num} + 2 ]
        title=$(sed -n "${title_line}"p page_${line}.html|awk '{print $1}')
        echo -e "${title} \t ${url}" >> title.txt
    done
done

第6章 shell流程控制之while循环

1.while循环使用场景

1.for是明确知道要循环多少次,while可以在不知道要循环多少次的场景下使用
2.比如如果用户输入错了,可以尝试重新输入,而不是退出
3.比如除非用户输入了退出指令才退出,否则一直不退出

2.while循环基本语法

while 条件测试      #如果条件成立,则执行循环
do
    循环执行的命令
done

3.举例

直到满足条件退出

#!/bin/bash

num=0

while [ ${num} -lt 10 ]
do
    echo "num is ${num}"
    num=$[ ${num} + 1 ]
done

从文件里读取数据

方法1:

exec < test.txt
while read line
do
    echo $line
done

方法2:

while read line
do
    echo $line
done < test.txt

方法3:

cat test.txt|while read line
do
    echo $line
done

4.练习

计算器

需求:使用while输出如下格式

9*1 =9 
8*2 =16 
7*3 =21 
6*4 =24 
5*5 =25 
4*6 =24
3*7 =21
2*8 =16
1*9 =9

脚本1:

#!/bin/bash

num=9
while [ ${num} -ge 1 ]
do
    echo "$num * $num = $[ $num * $num ]"
    num=$[ $num -1 ]
done

脚本2:

#!/bin/bash

a=9
b=1
while [ ${a} -ge 1 ]
do
    echo "$a * $b = $[ $a * $b ]"
    a=$[ $a -1 ]
    b=$[ $b -1 ]
done

直到输对了才退出

需求:

1.提示用户输入账号
2.除非输入了root,否则一直提示输入

脚本:

#!/bin/bash
while [ "$user" != "root" ]
do
    read -p "请输入root:" user
done

从文本里获取要创建的用户名:密码:uid:gid

#!/bin/bash 

exec < name.txt
while read line 
do
    GROUP=$(echo ${line}|awk -F ":" '{print $1}')
    GID=$(echo ${line}|awk -F ":" '{print $4}')
    USER=$(echo ${line}|awk -F ":" '{print $1}')
    UID=$(echo ${line}|awk -F ":" '{print $3}')
    PASS=$(echo ${line}|awk -F ":" '{print $2}')
    groupadd ${GROUP} -g ${GID}
    useradd ${USER} -u ${UID} -g ${GID}
    echo ${PASS}|passwd --stdin
done

猜数字游戏

需求

1.随机生成一个1-100的数字 
2.要求用户输入的必须是数字 
3.友好提示,如果用户输入的数字比随机数大,则提醒大了,否则提醒小了
4.只有输入正确才退出,输入错误就一直循环 
5.最后统计猜了多少次

脚本:

#!/bin/bash

sj=$(echo $[$RANDOM%100 + 1])
count=0

while true
do
    read -p "来下注吧,请输入整数: " num
    count=$[ $count+1 ]
    if [ ! -z $(echo ${num}|sed -r 's#[0-9]+##g') ];then
        echo "你是zzy吗?"
        continue
    fi

    if [ $num == $sj ];then
       echo "您成功打爆了zzy的gt ${count}次! 正确数字为: $sj"
       exit
    fi

    if [ $num -gt $sj ];then
        echo "你输大了"
    else
        echo "你输小了"
    fi

done

不退出的菜单

#!/bin/bash

while true 
do
    read -p "请输入您的选择:" num
    case $num in
        1)
            echo "选择1"
            ;;
        2)
            echo "选择2"
            ;;
        3)
            echo "选择3"
            ;;
        exit)
            echo "bye"
            exit
            ;;
        *)   
            echo "选择1-3"
    esac
done

跳板机脚本

#!/bin/bash

trap "" HUP INT QUIT TSTP

while true
do
    echo "
        ===================
        |   1.lb-5        | 
        |   2.lb-6        |
        |   3.web-7       |
        |   4.web-8       |
        |   5.exit        |
        ===================
    "    
    read -p "请输入需要登陆的主机:" num
    case $num in
        1)
            ssh root@10.0.0.5 
            ;;
        2)
            ssh root@10.0.0.6
            ;;
        3)
            ssh root@10.0.0.7
            ;;
        4)
            ssh root@10.0.0.8
            ;;
        5)
            exit
            ;;
        *)
            continue 
    esac 
done

第7章 shell流程控制之循环控制

1.应用场景

1.有些时候我们可能希望在满足特定条件的情况下立刻终止循环,即使循环还没结束
2.比如如果输错3次密码就强制退出,如果输入了退出关键里立刻退出等

2.break

2.1 break解释

结束当前的循环,但会继续执行循环之后所有的代码

2.2 break举例

#!/bin/bash

for i in {1..3}
do
    echo "123"
    break
    echo "456"
done

echo "all is ok"

3.continue

3.1 continue解释

1.忽略本次循环剩余的代码,直接进入下次循环,直到循环结束
2.循环结束之后,继续执行循环以外的代码。

3.2 continue举例

#!/bin/bash

for i in {1..3}
do
    echo "123"
    continue
    echo "456" 
done

echo "all is ok"

4.exit

4.1 exit解释

遇到exit直接退出整个脚本,后面不管有多少命令都不执行

4.2 exit举例

#!/bin/bash

for num in {1..3}
do
    echo "123"
    exit
    echo "456"
done

echo "all is ok"

第8章 shell函数

1.函数的作用

函数的作用就是将需要重复运行的代码抽象出来然后封装成一个函数,后续需要使用的时候只需要引用函数就可以了,而不需要每次都写重复的内容。

2.函数的定义和调用

2.1 定义函数的两种方法

第一种方法

start(){
    command
}

第二种方法

function start(){
    command
}

2.2 函数调用的方法

start(){
    command
}

function stop(){
    command
}

start
stop

3.函数的传参

3.1 函数传参介绍

1.用户执行脚本传递的位置参数和函数传递的参数事两回事
2.函数执行的时候需要将位置参数传递给函数,这样才会将参数带入函数执行。

3.2 举例

#!/bin/bash

fun1() {
    case $2 in 
        +)
            echo "$1 + $3 = $[ $1 + $3 ]"
            ;;
        -)
            echo "$1 + $3 = $[ $1 + $3 ]"
            ;;
        x)
            echo "$1 * $3 = $[ $1 * $3 ]"
            ;;
        /) 
            echo "$1 + $3 = $[ $1 + $3 ]"
            ;;
        *)
            echo "bash $0 num1 {+|-|x|/} num2"
    esac
}

fun1 $1 $2 $3

4.函数的练习

4.1 编写nginx管理脚本

#!/bin/bash

UAGE(){
    echo "UAGE: bash $0 {start|stop|restart}"
}

start_nginx(){
    echo "nginx is start"
}

stop_nginx(){
    echo "nginx is stop"
}

case $1 in 
    start)
      start_nginx
      ;;
    stop)
      stop_nginx
      ;;
    restart)
      stop_nginx
      start_nginx
      ;;
    *)
      UAGE
esac

4.2 编写多极菜单

#!/bin/bash

#1级菜单
menu1(){
echo "
-----------------------------
1.Install Nginx
2.Install PHP
3.Install MySQL
4.Quit
-----------------------------
"
}


#2级菜单
menu2(){
echo "
-----------------------------
1.Install Nginx1.15
2.Install Nginx1.16 
3.Install Nginx1.17 
4.返回上一层
-----------------------------
"
}

#打印1级菜单
menu1

while true
do
    #选择1级菜单
    read -p "选择对应的数字:" num1

    case $num1 in 
       1)
           #打印2级菜单
           menu2 
           while true
           do
               read -p "请选择您要安装的Nginx版本: " num2
               case $num2 in 
                   1)
                       echo "Install Nginx1.15 is OK!"
                       ;;
                   2)
                       echo "Install Nginx1.16 is OK!"
                       ;;
                   3)
                       echo "Install Nginx1.17 is OK!"
                       ;;
                   4)
                       clear
                       menu1
                       break
                       ;;
                   *)
                       continue
               esac 
           done
           ;;    
       2)
           echo "Install PHP"
           ;;
       3)
           echo "Install Mysql"
           ;;
       4)
           exit
           ;;
       *) 
           continue
    esac
done

4.3 编写跳板机脚本

#!/bin/bash

memu(){
echo"
    ===================
    |   1.lb-5        | 
    |   2.lb-6        |
    |   3.web-7       |
    |   4.web-8       |
    |   5.exit        |
    ===================
"    

trap "" HUP INT QUIT TSTP

while true
do
    memu
    read -p "请输入需要登陆的主机:" num
    case $num in
        1)
            ssh root@10.0.0.5 
            ;;
        2)
            ssh root@10.0.0.6
            ;;
        3)
            ssh root@10.0.0.7
            ;;
        4)
            ssh root@10.0.0.8
            ;;
        5)
            exit
            ;;
        *)
            continue 
    esac 
done

综合练习题-将用户登陆注册功能修改为函数版本

需求:

把ATM机的用户登陆注册用函数,case,while,continue实现所有功能

脚本:

#!/bin/bash

name_list=bank.txt
log=log.txt
time=$(date)

menu(){
    echo "
    ===========
    1.登陆
    2.注册
    ===========
    "
    read -p "请选择需要的操作:" menu 
}

check_login_name(){
    read -p "请输入用户名:" name
    grep -wo "${name}" ${name_list} >> /dev/null 2>&1
    if [ $? != 0 ];then
        echo "用户名不存在,请重新输入"
        check_login_name
    fi
}

check_login_pass(){
    read -p "请输入密码:" passwd_input
    passwd_user=$(awk -F":" "/^${name}\:/"'{print $2}' ${name_list})
    #passwd_user=$(sed -rn "s#${name}:(.*)#\1#g"p bank.txt)
    if [ ${passwd_input} == ${passwd_user} ];then
       echo "登陆成功!"
       echo "${time} ${name} 登陆成功!" >> ${log}
       exit
    else 
       echo "密码错误,请重新输入"
       echo "${time} ${name} 登陆失败!" >> ${log}
       check_login_pass
    fi
}

check_regist_name(){
    read -p "请输入注册用户名:" name
    grep -wo "${name}" ${name_list} >> /dev/null 2>&1
    if [ $? = 0 ];then
       echo "用户名已存在,再选一个吧"
       check_regist_name
    fi
}

check_regist_pass(){
    read -p "请输入密码:" passwd1
    read -p "请再次输入密码:" passwd2
    if [ ${passwd1} == ${passwd2} ];then
       echo "${name}:${passwd1}" >> ${name_list}
       if [ $? == 0 ];then
          echo "注册成功,请登录"
          echo "${time} ${name} 注册成功!" >> ${log}
          main
       else 
          echo "注册失败,请联系管理员"
          echo "${time} ${name} 注册失败!" >> ${log}
          exit
       fi
    else
       echo "两次输入的密码不一致,请重新输入"
       check_regist_pass
    fi
}

main(){
    while true
    do
        menu
        case ${menu} in 
        1)
            check_login_name
            check_login_pass
            ;;
        
        2)
            check_regist_name
            check_regist_pass
            ;;
        
        *)
            echo "请选择1-2的数字"
            main
    esac
    done
}

main

综合练习题-检查服务端口是否开启

exec < /scripts/ip-ports.txt 
while read line 
do 
    count=0
    nc -w 10 -z $line >> /tmp/ip.log 2>&1
    if [ $? -ne 0 ];then
	    for i in {1..3}
		do
            nc -w 10 -z $line >> /tmp/ip.log 2>&1
			if [ $? -ne 0 ];then
			   count=$[ ${count}+1 ]
			else
			   break
			fi
			
			if [ $count -eq 3 ];then
			   sleep 3
	           echo "民银牛app生产服务器${line}连接不通"|/usr/local/bin/mailx -v -s "test" 1214131982@qq.com >> /tmp/cron.log 2>&1
            fi
		done
	fi
done

第9章 shell数组-一致认为了解即可

1.数组介绍

数组主要是用来存值,只不过可以存储多个值。

2.数组分类

普通数组: 当一个数组定义多个值,需要取值时,只能通过整数来 取值 0 1 2 3 4 5
关联数组:他可以自定义索引名称,当需要取值时,只需要通过 数组的名称[索引] ---> 值

3.数组的赋值与取值

不用数组取内容

#!/bin/bash

name_list="user1 user2 user3 user4"
num=0
num2=0

for i in ${name_list}
do
    num=$[ $num + 1 ]
        if [ "$num" == "3" ];then
           echo $i
        fi
done

for i in ${name_list}
do
    num2=$[ $num2 + 1 ]
        if [ "$i" == "user3" ];then
           echo $num2
        fi
done

普通数组赋值

[root@m-61 ~/scripts]# books=(linux nginx shell)

关联数组赋值

注意: 必须先声明这是关联数组

[root@m-61 ~/scripts]# declare -A info

方法1:(数组名=( [索引1]=值1 [索引2]=值2) )

[root@m-61 ~/scripts]# info=([index1]=linux [index2]=nginx [index3]=docker [index4]='bash shell') 
[root@m-61 ~/scripts]# echo ${info[@]}
bash shell linux nginx docker
[root@m-61 ~/scripts]# echo ${!info[@]}
index4 index1 index2 index3

方法2:( 数组名[索引]=变量值 )

[root@m-61 ~/scripts]# info2[index1]=value1
[root@m-61 ~/scripts]# info2[index2]=value2
[root@m-61 ~/scripts]# info2[index3]=value3

取单个值

[root@m-61 ~/scripts]# echo ${books[0]}
linux
[root@m-61 ~/scripts]# echo ${books[1]}
nginx
[root@m-61 ~/scripts]# echo ${books[2]}
shell
[root@m-61 ~/scripts]# echo ${info2[index1]}
value1
[root@m-61 ~/scripts]# echo ${info2[index2]}
value2
[root@m-61 ~/scripts]# echo ${info2[index3]}
value3

取所有值

[root@m-61 ~/scripts]# echo ${books[@]}
linux nginx shell
[root@m-61 ~/scripts]# echo ${info2[@]}    
value1 value2 value3

取出所有索引

[root@m-61 ~/scripts]# echo ${!books[@]}
0 1 2
[root@m-61 ~/scripts]# echo ${!info2[@]}  
index1 index2 index3

数组取值小结

echo ${!array[*]}  #取关联数组所有键
echo ${!array[@]}  #取关联数组所有键
echo ${array[*]}   #取关联数组所有值
echo ${array[@]}   #取关联数组所有值
echo ${#array[*]}  #取关联数组长度
echo ${#array[@]}  #取关联数组长度

4.数组的遍历

需求1:统计/etc/passwd里每个用户shell出现的次数

脚本:

[root@m-61 ~/scripts]# cat set.sh 
#!/bin/bash

declare -A shell_num

exec < /etc/passwd 
while read line
do
    shell=$(echo $line | awk -F ":" '{print $NF}')

    #要统计谁,就将谁作为索引,然后让其自增
    let shell_num[$shell]++
done
    
#批量取值
for item in ${!shell_num[@]}
do
    echo "索引是: $item 出现的次数为: ${shell_num[$item]}"
done

执行结果:

[root@m-61 ~/scripts]# bash set.sh 
索引是: /sbin/nologin 出现的次数为: 18
索引是: /bin/sync 出现的次数为: 1
索引是: /bin/bash 出现的次数为: 1
索引是: /sbin/shutdown 出现的次数为: 1
索引是: /sbin/halt 出现的次数为: 1

需求2: 使用数组统计Nginx日志排名前10IP

脚本:

#!/bin/bash

declare -A IP

exec < bbs.xxxx.com_access.log 
while read line
do
    num=$(echo $line | awk '{print $1}')
    #要统计谁,就将谁作为索引,然后让其自增
    let IP[$num]++
done
    
#批量取值
for item in ${!IP[@]}
do
    echo "${item} ${IP[$item]}"
done

如果使用AWK处理,效率要比数组高很多倍

time awk '{Ip[$1]++} END { for (item in Ip) print Ip[item],item }' bbs.xxxx.com_access.log

5.数组练习题

需求:

文本内容:
a
b
c
1
2
3

处理后结果
1 a
2 b
3 c 

脚本1:

#!/bin/bash

num=$[ $(cat num.txt|wc -l)/2 ]

S1=($(sed -n "1,$num"p num.txt))
S2=($(sed -n "$[$num+1],\$"p num.txt))

for i in $(seq 0 $[$num-1])
do
    echo ${S2[$i]} ${S1[$i]}
done

脚本2:

#!/bin/bash

num=$[ $(cat num.txt|wc -l)/2 ]

for i in $(seq 1 $num)
do
    S1=$(sed -n "$i"p num.txt)
    S2_num=$[ $i + $num ]
    S2=$(sed -n "$S2_num"p num.txt)
    echo ${S2} ${S1}
done

第10章 通配符

1.通配符说明

1.通配符是shell的内置功能
2.作用是查找和匹配文件名称而不是文件里的内容
3.Linux大部分命令都支持通配符

2.特殊符号说明

.   代表了当前的目录
..  代表了上级目录
?  通配符
!   find取反  可以执行历史记录
!!  快速执行上一条命令
#   表示注释
|   管道,在正则中表示或者  a|c  |xargs   把管道传递的字符串变成了文件名
$   普通用户命令提示符  提取变量的内容 在正则中表示以什么结尾
~   代表了当前用户的家目录 cd ~    ll ~/file.txt
;   命令的组合
&&  并且 同时成立
||  或者 前面命令不成功执行||后面的命令
{}  生成序列
[]  不能生成序列 使用序列
*   所有
>   输出重定向 先清空后写入     PS:可能也是软链接
>>  追加输出重定向 追加到文件的最后一行
``  引用命令 把结果留在原地 让其他命令来使用 $()
$?  上一条命令的执行结果 0为成功 非0失败 
\   临时取消转译 还原本来的意义
^  正则表达式 以什么开头
/   根
''  单引号 所见即所得 不会解析单引号内的变量
""  和不加引号类似 解析变量
-   su - 切换和变量有关   cd - 切换到上一次所在的目录

3.练习

{} 创建有序的文件

mkdir oldboy && cd oldboy
touch stu{00..10}.txt

查找以stu开头的文件

ls stu*

查找以.txt结尾的文件

ls *.txt

查找以stu开头并且以.txt结尾的文件

ls -l stu*.txt
find -name "stu*.txt"

模糊匹配所有包含oldboy的文件

ls -l *oldboy*
find -type f -name "*oldboy*"

? 匹配任何一个字符/字母/数字

ll /bin/?

[] 匹配指定的内容

ls -l stu0[01234].txt
ls -l stu0[0123456789].txt
ls -l stu0[0-9].txt

查找所有以o或s开头的所有文件

ls -l [os]*

{} 生成序列

echo {0..9} {a..z}
echo {4,8}
echo {bbs,www,blog}

!^ 取反

ls -l stu0[!0-3].txt
ls -l stu0[^0-3].txt

> 标准正确输出重定向

作用: 把执行正确的结果重定向输出文件中 错误的不会被记录

echo 111 >1.txt
cat 1.txt
ccc 111 >1.txt
cat 1.txt

>> 标准正确追加重定向

作用: 把执行正确的结果追加输出到文件中 错误的不会被记录

echo 1111 >>1.txt
cat 1.txt
echo 1111 1>>1.txt
cat 1.txt 
ls 111111.txt
ls 111111.txt >>1.txt
cat 1.txt

2> 标准错误输出重定向

作用: 把执行错误的结果重定向输出文件中

echo 222 >1.txt
cat 1.txt
ccc 222 2>1.txt
cat 1.txt

2>> 标准错误输出追加重定向

作用: 把执行错误结果追加输出到文件中

echo 333 >>1.txt
cat 1.txt
ccc 333 2>>1.txt
cat 1.txt

2>&1 正确和错误的结果都输出到文件中

echo 2222 >1.txt 2>&1

$ 普通用户提示符 获取变量内容

[root@m01 ~/scriptes]# su - user1
[user1@m01 ~]$ 

`` $() 引用命令

[root@m01 ~/scriptes]# echo "$(date)"
Thu Aug 20 09:45:21 CST 2020
[root@m01 ~/scriptes]# echo `date`
Thu Aug 20 09:45:40 CST 2020

; 分隔多个命令 命令拼接

;和&&不一样,并没有逻辑关系,不管前面成不成功,都会执行

ls;pwd;ll

&& 前面命令执行成功才会执行后面的命令

ls /etc/passwd && echo OK

|| 前面命令执行不成功才会执行后面的命令

ls /etc/passwdddd && echo OK || echo NO

第11章 正则表达式

1.正则表达式解释

特定字符:

2.通配符和正则表达式的区分

1.三剑客awk sed grep egrep使用的都是正则,其他都是通配符
2.通配符是针对文件名,正则表达式是针对文件的内容
3.* ? [] 对于通配符来说都能代表任意字符
4.* ? [] 对于正则表达式来说只能代表这些符号前面的字符

3.贪婪匹配


第12章 三剑客命令之grep

1.创建测试文本

cat > oldboy.txt << EOF
I am zhangya teacher!
I teach linux.
test

I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.
EOF

2.正则练习题

1. ^ 查找以什么开头的行

[root@m-61 ~]# grep "^m" oldboy.txt 
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

2. $ 查找以什么结尾的行

[root@m-61 ~]# grep "s$" oldboy.txt   
I like swimming, football, basketball, video games
I like Apple's devices

3. ^$ 查找和排除空行

[root@m-61 ~]# grep -n "^$" oldboy.txt    
4:
[root@m-61 ~]# grep -vn "^$" oldboy.txt 
1:I am zhangya teacher!
2:I teach linux.
3:test
5:I like swimming, football, basketball, video games
6:I like Apple's devices
7:my blog is https://www.cnblogs.com/alaska/
8:my site is https://www.jianshu.com/u/ee1c7fcea5b0
9:my qq num is 526195417.
10:my phone is 15321312624.

4. . 任意一个字符 不会匹配空行 包含空格

默认贪婪匹配,会匹配所有的内容:

[root@m-61 ~]# grep "." oldboy.txt 
I am zhangya teacher!
I teach linux.
test
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

默认的贪婪匹配会把所有内容都匹配完后一起输出,如果我们想看每一次的匹配内容可以使用-o参数

[root@m-61 ~]# grep "." oldboy.txt -o
I
 
a
m
 
z
........内容略........

匹配一个任意字母的单词,默认会打印匹配到的行

[root@m-61 ~]# grep "li.e" oldboy.txt 
I like swimming, football, basketball, video games
I like Apple's devices

匹配多个任意字母的单词

[root@m-61 ~]# grep "li.." oldboy.txt    
I teach linux.
I like swimming, football, basketball, video games
I like Apple's devices

5.\ 转义特殊字符

假如有需求:查找所有以 . 结尾的内容

如果不转义特殊字符的话,. 这个符号会被认为是正则表达式

[root@m-61 ~]# grep ".$" oldboy.txt 
I am zhangya teacher!
I teach linux.
test
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

如果我们想过滤的是.的话,需要使用\将正则表达式符号转换为普通的符号

[root@m-61 ~]# grep "\.$" oldboy.txt 
I teach linux.
my qq num is 526195417.
my phone is 15321312624.

6.[ ]匹配字符

匹配包含abc任意字符串

[root@m-61 ~]# grep "[abc]" oldboy.txt 
I am zhangya teacher!
I teach linux.
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0

匹配a-Z的任意字符

[root@m-61 ~]# grep "[a-Z]" oldboy.txt    
I am zhangya teacher!
I teach linux.
test
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

匹配0-9的字符串

[root@m-61 ~]# grep "[0-9]" oldboy.txt    
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

[ ]匹配特殊字符,[ ]里大部分字符没有特殊含义,写什么就查找什么

查找包含 ! 和 . 的内容

[root@m-61 ~]# grep '.!' oldboy.txt   
I am zhangya teacher!

[root@m-61 ~]# grep '[.!]' oldboy.txt       
I am zhangya teacher!
I teach linux.
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

[^ ] 取反,在[ ]里面^变成了取反的意思


3.拓展正则练习

1.拓展正则说明

{}
|
()

2.{ } 限定位数

{ n }表示前一个字符最多显示n次

练习

[root@m-61 ~]# egrep "e{2}" oldboy.txt       
my site is https://www.jianshu.com/u/ee1c7fcea5b0

{ n,m }表示

4.工作需求

排除配置文件中的空行


排除配置文件中注释的行


第13章 三剑客命令之sed

1.sed介绍

sed是Stream Editor(字符流编辑器)的缩写,简称流编辑器.什么意思呢?就像流水线,sed就像一个车间一样,文件中的每行字符都是原料,运到sed车间然后经过一系列的加工处理,最后从流水线上下来就变成货物了. 

2.sed应用场景

1.查找和过滤文件内容
2.替换文件内容
3.脚本里可以用来检查输入是否纯英文或者纯数字

3.sed书写格式

sed [options] [sed-commands] [input-file]
sed [选项]	  [sed命令]		 [输入文件]

4.sed命令执行过程

sed从文件或管道读取一行,处理一行,输出一行;
再读取一行.再处理一行.在输出一行

5.sed选项说明

options常用选项:

-n		取消默认的sed软件的输出,常与sed命令的p连用
-e    一行命令语句可以执行多条sed命令
-r    使用正则拓展表达式,默认情况sed只识别基本正则表达式
-i    直接修改文件内容,而不是输出终端,如果不使用-i选项sed软件只是修改在内存中的数据,并不影响磁盘上的文件

commands常用命令:

a			追加,在指定行后添加一行或多行文本
c			取代指定的行
d     删除指定的行
i     插入,在指定的行前添加一行或多行文本
p     打印内容,通常和-n一起使用
s     取代 s#要替换的内容#替换的内容#g 

特殊符号:

!     对指定行以外的所有行应用命令
=     打印当前行号
;     实现一行命令语句可以执行多条sed命令

6.sed匹配行地址范围解释

10{sed-commands}					对第10行操作
10,20{sed-commands}				对10到20行操作,包括第10,20行
10,+20{sed-commands}			对10到30(10+20)行操作,包括第10,30行
1~2{sed-commands}					对1,3,5,7...行操作
10,$(sed-commands)				对10到最后一行($代表最后一行)操作,包括第10行
/oldboy/{sed-commands}		对匹配oldboy的行操作
/oldboy/,/Alex/{sed-commands}	对匹配oldboy的行到匹配Alex的行操作
/oldboy/,${sed-commands}	对匹配oldboy的行到最后一行操作
/oldboy/,10{sed-commands}	对匹配oldboy的行到第10行操作.注意:如果前10行没有匹配到oldboy,sed软件会显示10行以后的匹配oldboy的行,如果有
1,/Alex/{sed-commands}		对第一行匹配到Alex的行操作
/oldboy/,+2{sed-commands}	对匹配oldboy的行到其后的2行操作

7.sed练习题-增

测试文本

cat > oldboy.txt << EOF
I am zhangya teacher!
I teach linux.
test

I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.
EOF

单行增加

sed '2a good good study day day up' oldboy.txt 

多行增加

sed '2a good good study day day up\nyou can up no can no bb' oldboy.txt 

优化SSH配置

sed '3i Port 52113\nPermitRootLogin no\nPerminEmptyPasswords no\nUseDNS no\nGSSAPIAuthentication no' oldboy.txt

8.sed练习题-删

删除指定行

sed '2d' person.txt 

删除连续多行

sed '2,5d' person.txt 

删除指定的几行

sed '2d;5d' person.txt 

使用正则删除指定行

sed '/zhangyao/d' person.txt 

使用步长删除行

sed '1~2d' person.txt 

删除指定行往后的N行

sed '1,+2d' person.txt 

取反删除

sed '2,3!d' person.txt

9.sed练习题-改

按行替换

sed '2c 106,dandan,CSO' person.txt 

文本替换

sed 's#zhangyao#XXXXX#g' person.txt 

修改指定行配置文件

sed '3s#0#9#' person.txt 

变量替换

x=1
y=2
sed 's#$x#$y#g' person.txt 

单引号和双引号区别


黑科技-分组替换

echo "I am oldboy teacher"|sed 's#^.*am \([a-z].*\) tea.*$#\1#g'
echo "I am oldboy teacher"|sed -r 's#I (.*) (.*) teacher#\1\2#g'

使用sed取出eth0的IP地址

ifconfig eth0|sed -rn '2s#^.*r:(.*) Bc.*$#\1#gp'

批量重命名文件

touch stu_102999_{1..5}_finished.jpg
ls|sed -r 's#(.*)_(.*)_(.*)_(.*)(\..*)#\1_\2_\3\5#g'
ls|sed -r 's#(.*)_(.*)_(.*)_(.*)(\..*)#mv & \1_\2_\3\5#g'
ls|sed -r 's#(.*)_(.*)_(.*)_(.*)(\..*)#mv & \1_\2_\3\5#g'|bash
ls *.jpg|sed -r 's#(^.*)_finished.*$#mv & \1.jpg#'
ls stu*|xargs -n1|sed -r "s#(^.*[1-9]).*(.jpg)#mv & \1\2#g"
ls|awk -F "_finished" '{print "mv",$0,$1$2}'
rename "_finished" "" *.jpg

10.sed练习题-查

按行查询

sed '2p' person.txt 

查询多行

sed -n '2,3p' person.txt 

按步长查询

sed -n '1~2p' person.txt 

按字符串查询

sed -n '/CTO/p' person.txt 
sed -n '/CTO/,/CFO/p' person.txt 

混合查询

sed -n '2,/CFO/p' person.txt 

多虑多个字符

sed -rn '/oldboy|feixue/p' person.txt

11.sed练习题-修改文件

更改文件

sed -i 's#zhangya#NB#g' person.txt 

更改文件同时备份

sed -i.ori 's#zhangya#NB#g' person.txt 

文件另存为

sed 'w output.txt' person.txt

12.sed练习题-替换

替换命令说明

sed '[地址范围|模式范围] s#[被替换的字符串]#[替换后的字符串]#[替换标志]' [输入文件]
命令说明:
1.[地址范围|模式范围]:可选选项,如果没有指定,sed软件将在所有行上进行替换。
2.“s”即执行替换命令substitute
3.被替换的字符串,可以是一个正则表达式
4.替换后的字符串,只能是一个具体的内容
5.替换标志:可选选项,包括前面详述的全局标志g,还有数字标志(1,2,3...),打印标志p,写标志w,忽略大小写标志i,执行命令标志e,根据需要可以把一个或多个替代标志组合起来使用。

Ms# # #Ng 的使用
Ms ==》对第M行处理,无g替换标志,只处理第一行匹配,有g替换标志则对第M行全部替换
Ng ==》对每一行,从第N出开始替换
Ms,Ng组合表示只对第M行从第N处匹配开始替换

测试语句

cat > num.txt << EOF
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
EOF

替换

sed 's#1#0#2g' num.txt
sed ' 2s#1#0#g ' num.txt 
sed '2s#1#0#2g' num.txt 

数字标志

测试样本

cat > person.txt <<EOF
101,Zhangya,CEO
102,Liangzai,CTO
103,Json,COO
104,Jangli,CFO
105,Xiayan,CIO
EOF

替换指定行第N次匹配内容并忽略大小写

sed '1s#a#XXX#2i' person.txt
sed '1s#o#XXX#3i' person.txt

打印标志

注意:这里的P是附属于s命令的。

首先将zhangya替换成NB,然后使用p标志打印到屏幕,同时使用-n选项取消默认输出,

这个标志可以实现修改完文本直接显示修改的结果

sed -n 's#zhangya#NB#p' person.txt

写标志

写标志w,当替换操作执行成功后,它把替换后的结果保存到文件中,这个也类似于前面学过的sed命令w。

sed -n 's#zhangya#NB#w output.txt' person.txt

忽律大小写标志

前面我们学习过的匹配功能都是要将匹配字符原样写上,是区分大小写。这也是默认的功能,但是我们可以使用忽略大小写标志i来忽略大小写。

sed 's#alex#NB#i' person.txt

执行命令标志

执行命令标志e,可以将模式空间中的任何内容当作shell命令执行,并把命令执行的结果返回模式空间。

sed 's#^#ls -lh #e' file.txt 

命令说明:注意,“^”尖角号代表每一行最头头的地方,后面的命令后有一个空格要注意!

其他替换标志

替换标志	含义
\l			在替换字符中shiyong\l标志时,他会把紧跟其后面的1个字符当做小写字符来处理
\L			在替换字符中shiyong\L标志时,他会把后面所有的字符都当作小写字符来处理
\u			在替换字符中使用\u标志时,他会把紧其后面的1个字符当作大写字符来处理
\U			在替换字符中使用\U标志时,他会把后面所有的字符都当作大写字符来处理。
\E			需要\U或\L一起使用,他将关闭\U或\L的功能

13.sed练习题-特殊符号

获取行号

sed "=" person.txt

只打印1到3的行号,同时打印输入文件中的内容

sed "1,3=" person.txt

打印匹配的行所在的行号

sed "/zhangya/=" person.txt

只显示行号不显示行的内容

sed -n "/zhangya/=" person.txt

显示文件总行数

sed -n "$=" person.txt

改进方法

sed "=" person.txt|sed 'N;s#\n# #'

打印不可见字符l

sed -n 'l' person.txt

14.sed执行多条命令

面试题:用一条sed语句实现删除文件的第三行到末尾的数据,并把剩余的数字10替换为 01

sed -e '3,$d' -e 's#10#01#' person.txt

分号;的使用

sed '3,$d;s#10#01#' person.txt

从sed脚本读取

创建脚本

cat > person.sed << 'EOF' 
3,$d
s#10#01#
EOF

执行

sed -f person.sed person.txt

面试题:一个文件100行,把5,35,70行单独拿出来

sed -n '5p;35p;70p' /etc/services 

特殊符号

说明:

{command}可以把多个命令括起来,用来处理单个地址或者地址范围。

需求:打印文件的第2行到第4行,并只显示这3行的行号

有问题的输出

sed -n '2,4p;=' person.txt

正确的输出

sed -n '2,4{p;=}' person.txt

15.sed模拟其他命令

sed -n '/zhangyao/p' person.txt
sed -n '/oldboy/ !p' person.txt
sed '3,$d' person.txt
sed '1,2p' person.txt
sed '2q' person.txt
sed -n "$=" person.txt

15.综合练习题

1.取出/etc/passwd 的第一列
1.取IP的几种方法
1.调换passd第一列和第三列位置

答案

egrep -o "^[^:]+" /etc/passwd
egrep -o "^[a-zA-Z0-9]+" /etc/passwd
sed 's#:x:.*$##g' passwd 
sed -r 's#:.*$##g' passwd
sed -r 's#(.):.*$#\1#g' passwd
sed -r 's#(^[^:]+).*$#\1#g' passwd 
sed -r 's#(^.*):x.*$#\1#g' passwd
sed -r 's#(^[a-zA-Z0-9#]+).*$#\1#g' passwd
ifconfig eth0|egrep -o "[0-9]+.[0-9]+.[0-9]+.[0-9]+"|head -1
ifconfig eth0|sed -nr '2s#^.*r:(.*) B.*$#\1#gp'
ifconfig eth0|sed -nr '2s#^.*addr:(.*)  Bca.*#\1#gp'
ifconfig eth0|sed -rn '2s#^.*r:|  Bc.*$##gp' 
ifconfig eth0|sed -rn '2s#^[a-zA-Z :]+(.*)  B.*$#\1#gp'
ifconfig eth0|sed -rn '2s#^[^0-9]+(.*)  B.*$#\1#gp'
ifconfig eth0|awk -F "[: ]" 'NR==4{print $3}'
ifconfig eth0|awk -F "addr:|Bcast" 'NR==2{print $2}'
awk -F ":" 'BEGIN{OFS=":"}{print $7,$2,$3,$4,$5,$6}' root.txt 
sed -r 's#(.*)(:x.*t:)(.*)#\3\2\1#g' root.txt 
sed -r 's#(^[a-zA-Z0-9#]+)(:.*:)(.*$)#\3\2\1#g' passwd
sed -r 's#(^[^:]+)(:.*:)(.*$)#\3\2\1#g' passwd

第14章 三剑客命令之AWK

posted @ 2020-08-22 23:53  #!/bin/bash  阅读(899)  评论(0编辑  收藏  举报