【快速入门并掌握shell脚本编程】

shell脚本一学就会:

提示:通过此博文可快速掌握shell的基本用法

  • 用心学习,一天即可掌握shell

一、SHELL基础:

什么是shell

shell是用户与linux内核之间的解释器

[root@ztj ~]# cat /etc/shells 				#查看shell解释器
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
[root@ztj ~]# usermod -s /bin/tcsh test

使用chsh更改解释器

[root@ztj ~]# chsh  -s  /bin/bash test			#test用户解释器修改为bash
Changing shell for test.
Shell changed.

覆盖重定向

[root@ztj ~]# ls  >  a.txt

追加重定向

[root@ztj ~]# ls  >>  a.txt

收集正确信息

[root@ztj ~]# ls xxyyzz.txt > b.txt				#失败,> 收集正确信息

ls: 无法访问xxyyzz.txt: 没有那个文件或目录

收集错误信息

[root@ztj ~]# ls xxyyzz.txt  2>  b.txt				#正确,2> 收集错误信息

收集正确和错误的信息

[root@ztj ~]# ls  a.txt  xxyzz.txt  &>  b.txt			#收集所有信息

管道

[root@ztj ~]# yum list | grep bash				#过滤跟bash相关的包

临时使用ksh解释器

[root@ztj ~]# ksh			#临时使用ksh解释器
# ls				#查看目录没有颜色
# ls /
# clear			#清屏,并且无法使用上下左右键
# exit			#退出

二、脚本的设计与运行

脚本构成

[root@ztj ~]# chmod  +x /root/shell/test01/first.sh		#添加执行权限
[root@ztj test01]# vim /root/shell/test01/first.sh
#!/bin/bash							#指定解释器
#This a test program for shell.			#这是一个测试性的程序
echo "Hello World"

规范脚本的构成

脚本声明(需要的解释器、作者信息等)
注释信息(步骤、思路、用途、变量含义等)
可执行语句(操作代码)

脚本在执行的时候要有执行(x)权限,否则会报错

[root@ztj test01]# chmod -x first.sh 
[root@ztj test01]# ./first.sh			#报错
-bash: ./first.sh: 权限不够
[root@ztj test01]# /root/shell/test01/first.sh	#报错
-bash: /root/shell/test01/first.sh: 权限不够

不需要文件有可执行权限,来执行脚本

[root@ztj test01]# sh first.sh 	//开启子进程
[root@ztj test01]# bash first.sh  //开启子进程
[root@ztj test01]# ksh first.sh	//开启子进程
[root@ztj test01]# vim tmp.sh
#!/bin/bash
sleep 100
[root@ztj test01]# sh tmp.sh	#运行脚本

定义变量

[root@ztj ~]# a=11
[root@ztj ~]# echo $a		#调用变量
[root@ztj ~]# a=33		#变量名已经存在,再次赋值,里面的内容会被覆盖
[root@ztj ~]# echo $a

名变量的命令

变量名不能使用特殊符号,会报错,不能以数字开头,可以是数字,字母,下划线

[root@ztj ~]# echo ${x}6.5
[root@ztj ~]# echo ${x}7.7

取消变量

[root@ztj ~]# unset x
[root@ztj ~]# echo $x
P2 系统预设变量

环境变量

[root@ztj ~]# echo $PATH		#命令搜索的路径变量
[root@ztj ~]# echo $PWD		#返回当前工作目录
/root
[root@ztj ~]# echo $USER		#显示当前登录的用户
root
[root@ztj ~]# echo $UID		#显示当前用户的uid
0
[root@ztj ~]# echo $HOME		#显示当前用户的家目录
/root
[root@ztj ~]# echo $SHELL		#显示当前的SHELL
/bin/bash	

位置变量

存储脚本时执行的参数
$1 $2 $3 …$9 ${10} ${11} … #从10开始位置变量需要加{}

[root@ztj ~]# vim /root/shell/test01/vars.sh
#!/bin/bash
echo $1
echo $2
echo $3
[root@ztj ~]# chmod +x /root/shell/test01/vars.sh 
[root@ztj ~]# /root/shell/test01/vars.sh aa bb cc 	#aa bb cc 分别对应脚本里面的$1 $2 $3
aa
bb
cc

编写一个user标本,使用它创建系统用户

[root@ztj ~]# vim /root/shell/test01/user.sh
#!/bin/bash
useradd "$1"
echo "$2" | passwd --stdin $1
[root@ztj ~]# chmod +x /root/shell/test01/user.sh
[root@ztj ~]# /root/shell/test01/user.sh tom 123
[root@ztj ~]# /root/shell/test01/user.sh jim 123

预定义变量

用来保存脚本程序的执行信息,可以直接使用这些变量,但是不能为这些变量赋值

$?:执行上一条命令的返回状态,0为正确,非0为错误

[root@ztj ~]# ls /etc/hosts
/etc/hosts
[root@ztj ~]# echo $?		#返回值为0,正确
0
[root@ztj ~]# ls /xxxxxyyyy		#返回值为非0,失败
ls: 无法访问/xxxxxyyyy: 没有那个文件或目录
[root@ztj ~]# echo $?
2

其他几个预定义变量的测试

[root@ztj ~]# vim /root/shell/test01/pre.sh
#!/bin/bash
echo $0		#执行脚本的名字
echo $$		#当前脚本的进程号
echo $#		#位置变量的个数
echo $*		#所有位置变量

三、变量的扩展应用

多种引号的区别

1)双引号的应用
使用双引号可以界定一个完整字符串。

[root@ztj ~]# touch a b c 		#创建了三个文件
[root@ztj ~]# touch "a b c"		#创建1一个文件
[root@ztj ~]# ls -l
[root@ztj ~]# rm -rf  a b c
[root@ztj ~]# rm -rf  "a b c"

2)单引号的应用
界定一个完整的字符串,并且可以实现屏蔽特殊符号的功能。
当双引号里面有变量时,会被扩展出来,也就是会取变量的值

[root@ztj ~]# hi="world"
[root@ztj ~]# echo "$hi"		#成功
world
[root@ztj ~]# echo '$hi'		#失败,当成一个字符串
$hi

当没有特殊符号时,单引号和双引号的含义是一样的

[root@ztj ~]# touch "a b c"
[root@ztj ~]# touch 'c d e'

3)反撇号或 ( ) 的 应 用 使 用 反 撇 号 或 ()的应用 使用反撇号或 ()使()时,可以将命令执行的标准输出作为字符串存储,因此称为命令替换。

[root@ztj ~]# grep root /etc/passwd
[root@ztj ~]# test=`grep root /etc/passwd`
[root@ztj ~]# echo $test
[root@ztj ~]# test2=$(grep root /etc/passwd)
[root@ztj ~]# echo $test

read命令定义变量

使用read命令从键盘读取变量值
1)read基本用法
执行后从会等待并接受用户输入(无任何提示的情况),并赋值给变量:

[root@ztj ~]# read iname
123
[root@ztj ~]# echo $iname
123

[root@ztj ~]# read iname
test
[root@ztj ~]# echo $iname
test

虽然可以赋值。但是屏幕上没有任何提示信息,在未来写脚本的时候不太方便,可以加上-p选项,给出提示
案例:创建一个脚本,通过read定义变量创建用户,更改密码

[root@ztj ~]# vim /root/shell/test01/read.sh
#!/bin/bash
read -p "请输入用户名:" name
read -p "请输入密码:"   pass
useradd $name
echo "$pass" | passwd --stdin $name
[root@ztj ~]# chmod +x /root/shell/test01/read.sh
[root@ztj ~]# /root/shell/test01/read.sh 
请输入用户名:user2
请输入密码:a
更改用户 user2 的密码 。
passwd:所有的身份验证令牌已经成功更新。

但是此时密码是名为显示的,不安全,可以使用-s参数,不显示终端输入的信息

[root@ztj ~]# vim /root/shell/test01/read.sh
……
read  -s -p "请输入密码:"   pass
……

-t可指定超时秒数

[root@ztj ~]# read -t 3 iname		#3秒不输入直接退出

全局or局部

局部变量

[root@ztj ~]# x=11
[root@ztj ~]# echo $x		#成功
11
[root@ztj ~]# bash	#开启子进程,测试
[root@ztj ~]# echo $x	#没有数据
[root@ztj ~]# exit

全局变量

[root@ztj ~]# export Y=22
[root@ztj ~]# echo $Y
22
[root@ztj ~]# bash		#开启子进程,测试
[root@ztj ~]# echo $Y		#成功
22
[root@ztj ~]# bash		#开启子进程,测试
[root@ztj ~]# echo $Y		#成功
22
[root@ztj ~]# exit
[root@ztj ~]# exit

四、shell中的运算

整数运算

使用$[ ]或$(( ))表达式,支持取变量的值进行运算

[root@ztj ~]# x=2
[root@ztj ~]# y=3
[root@ztj ~]# echo $[x+y]
5
[root@ztj ~]# echo $[x*y]
6
[root@ztj ~]# echo $((x*y))
6
[root@ztj ~]# echo $((3*2))
6

变量的自增/减等操作

[root@ztj ~]# x=2
[root@ztj ~]# echo $[x+=4]
6
[root@ztj ~]# echo $x
6
[root@ztj ~]# echo $[x+=1]
7
[root@ztj ~]# echo $[x*=3]
21
[root@ztj ~]# echo $x
21

关于自增/减也可以使用let命令,但是他不会返回计算的结果,但是可以结合echo命令来查看

[root@ztj ~]# i=2
[root@ztj ~]# let i+=2
[root@ztj ~]# echo $i
4
[root@ztj ~]# let i*=3
[root@ztj ~]# echo $i
12

小数运算

bc计算器

[root@ztj ~]# echo "1.2+3.3" | bc
4.5
[root@ztj ~]# echo "1.2+3.3;3.8+2.1" | bc		#多组运算
4.5
5.9
[root@ztj ~]# echo "scale=2;10/2" | bc		#指定小数点位数
5.00

小数的比较 表达式成立返回1,否则返回0

[root@ztj ~]# echo "1>2" | bc 
0
[root@ztj ~]# echo "5>2" | bc 
1
[root@ztj ~]# echo "5==2" | bc 		#等于
0
[root@ztj ~]# echo "5!=2" | bc 		#不等于
1

设置字体颜色

[root@ztj ~]# echo -e "\033[32mOK\033[0m"		# \033[开启颜色属性,32m为黑色,\033[0m 关闭颜色,还以黑色显示

设置背景颜色

[root@ztj ~]# echo -e "\033[42mOK\033[0m"

字体颜色
字体颜色是由一个ANSI 转义编码来控制的。该控制编码会嵌入字符流中并发送给终端仿真器。但是,该控制编码不会被“打印”到屏幕上,而是会被终端解释为一个指令。正如我们在上表看到的字符序列, 这个 [ 和 ] 序列被用来封装这些非打印字符。一个 ANSI 转义编码以一个八进制033(这个编码是由 退出按键产生的)开头,其后跟着一个可选的字符属性(0:正常、1:黑体、4:下划线、5:闪烁、7:反向(前景色和背景色反转)),在之后是一个指令。
序列 文本颜色 序列 文本颜色

\033[0;30m	黑色	\033[1;30m 	深灰色
\033[0;31m	红色	\033[1;31m 	浅红色
\033[0;32m 	绿色	\033[1;32m 	浅绿色
\033[0;33m 	棕色	\033[1;33m 	黄色
\033[0;34m 	蓝色	\033[1;34m 	浅蓝色
\033[0;35m 	粉红	\033[1;35m 	浅粉色
\033[0;36m 	青色	\033[1;36m 	浅青色
\033[0;37m 	浅灰色	\033[1;37m 	白色

背景颜色
除了字体颜色,我们也可以设置字体的背景颜色。同样是通过转义的控制编码来实现,下表是背景颜色的控制编码。

\033[0;40m 	黑色	\033[1;44m 	蓝色
\033[0;41m 	红色	\033[1;45m 	粉红
\033[0;42m 	绿色	\033[1;46m 	青色
\033[0;43m 	棕色	\033[1;47m 	浅灰色

五、条件测试

条件测试的基本用法

语法格式

使用“test 表达式”或者[ 表达式 ]都可以,表达式两边至少要留一个空格。
字符串测试 是否为空 [ -z 字符串 ]
#是,返回值为0 #否,返回值1

等于[ 字符串1 == 字符串2 ]
不等于[ 字符串1 != 字符串2 ]

整数值比较

格式:[ 整数值1 操作符 整数值2 ]

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

参与比较的必须是整数(可以调用变量),比较非整数值时会出错

文件状态的测试 [ 操作符 文件或目录 ]

e 判断对象是否存在(不管是目录还是文件),存在则结果为真
-d 判断对象是否为目录(存在且是目录),是则为真
-f 判断对象是否为文件(存在且是文件)是则为真
-r 判断对象是否可读,是则为真
-w 判断对象是否可写,是则为真
-x 判断对象是否具有可执行权限,是则为真

控制操作符

组合多个命令: ; && ||

;		按照顺序进行命令的执行,命令之间没有任何逻辑关系
&&		当符号前的命令执行成功,才执行符号后的命令
||		当符号前的命令执行失败,才执行符号后的命令,前面命令执行成功,后面命令不执行,二选一执行

if语句

if单分支的语法组成:

if  条件测试
then  
命令序列
fi

if双分支的语法组成:既可以执行成功的命令,也可以执行失败的命令

if  条件测试
then
命令序列1
else  
命令序列2
fi

if多分支的语法组成: 针对多个条件分别执行不同的操作

if    条件测试1 ;then  
命令序列1
elif  条件测试2 ;then  
命令序列2
……
else
命令序列n
fi

for循环

for循环语法格式
根据变量的不同取值,重复执行命令序列(for循环使用场合,在知道循环次数的时候使用)
for循环的语法结构:
第一种格式

for  变量名  in  值列表
do
    命令序列
done

第二种格式

for  ((初值;条件;步长))
do
    命令序列
done

案例:

[root@ztj test02]# vim for_demo2.sh
#!/bin/bash
for ((i=1;i<=5;i++))
do
        echo "I am $i"
done

while循环

语法结构及特点
反复测试条件,只要成立就执行命令序列(死循环或者条件循环时使用【不知道循环次数时】)

while循环的语法结构:
while  条件测试
do
    命令序列
done

死循环语法结构

while :
do
    命令序列
done

case语句

case语法格式

case $x in
A|b|c)
        echo "redhat";;
Redhat|ESXi)
        echo "esxi";;
*)

数组

数组也是一个变量,是一个有点特殊的变量。存储多个数据的集合就是数组。

[root@ztj test03]# test=(aa bb cc)				#定义数组
[root@ztj test03]# echo ${test[0]}				#调用数组的值,数组起始下标为0
aa

六、SHELL函数

语法格式

在Shell脚本中,将一些需重复使用的操作,定义为公共的语句块,即可称为函数。通过使用函数,可以使脚本代码更加简洁,增强易读性,提高Shell脚本的执行效率。
格式1:

function  函数名 {
    命令序列
    .. ..
}

格式2:

函数名() {
    命令序列
    .. ..
}

函数的调用
直接使用“函数名”的形式调用,如果该函数能够处理位置参数,则可以使用“函数名 参数1 参数2 … …”的形式调用。注意:函数的定义语句必须出现在调用之前,否则无法执行。

加法器

传递参数计算两个数字之和

[root@ztj test03]# add(){
> echo $[$1+$2]
> }

grep扩展:

grep  -q	 # -q无论找得到数据还是找不到数据,都不显示在屏幕上面
grep  -w     #看做的是一个独立的参数
[root@ztj test03]# echo "abc" | grep -w a		#找不到

七、字符处理与变换初始化

字符串处理机制

子串截取

格式:${变量名:起始位置:长度}

使用${}方式截取字符串时,起始位置是从0开始的。

子串替换

格式:${变量名/old/new}		#替换1个结果
格式:${变量名//old/new}		#替换全部结果
  • 注:此时替换是只是显示替换的值在屏幕上面,实际上,变量的值并没有替换
    如果想要把替换的值保存下来,重新赋值给某个变量即可

子串掐头

格式:${变量名#关键词}		# #号代表从左向右,最短匹配删除
格式:${变量名##关键词}		# ##号代表从左向右,最长匹配删除
  • 注:对变量掐头不会改变变量原有的值
[root@ztj ~]# A=`head -1 /etc/passwd`		#定义一个变量
[root@ztj ~]# echo ${A}
root:x:0:0:root:/root:/bin/bash
[root@ztj ~]# echo ${A#*:}		#从左到右,最短匹配删除,看第一个冒号位置,*通配符
x:0:0:root:/root:/bin/bash
[root@ztj ~]# echo ${A##*:}	#从左到右,最长匹配删除,看最后一个冒号位置
/bin/bash

子串去尾

格式:${变量名%关键词}		# %号代表从右向左,最短匹配删除
格式:${变量名%%关键词}		# %%号代表从右向左,最长匹配删除
  • 注:对变量去尾不会改变变量原有的值
[root@ztj ~]# echo ${A}
root:x:0:0:root:/root:/bin/bash
[root@ztj ~]# echo ${A%:*}			#从右向左,最短匹配删除
root:x:0:0:root:/root
[root@ztj ~]# echo ${A%%:*}		#从右向左,最长匹配删除
root

变量初始化

判断一个变量是否有值,如果有值则返回该变量的值,如果无值则返回赋予的初始化的值

格式:${变量:-关键词}
[root@ztj test03]# X=123		#变量赋值
[root@ztj test03]# echo ${X:-abc123}		#变量X有值,返回变量的值
123
[root@ztj test03]# echo ${XXAB:-123456}	#变量XXAB无值,赋予初始化的值
123456

随机密码

随机密码案例1
#所有密码的可能性是26+26+10=62(0-61是62个数字)

[root@ztj test03]# vim pass.sh
#!/bin/bash
key="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
pass=""
for i in {1..10}
do
        num=$[RANDOM%${#key}]
        tmp=${key:num:1}
        pass=${pass}${tmp}
done
echo $pass

随机密码案例2

[root@ztj test03]# uuidgen		#生成的UUID号可以作为密码
[root@ztj test03]# openssl rand -base64 10	#rand是随机的意思,10和密码的长度有关,数字越大,密码越长,但是不是密码的位数

随机密码案例3
使用随机设备文件/dev/random /dev/urandom

[root@ztj test03]# strings /dev/random		#随机提取字符串,random提取的较慢
[root@ztj test03]# strings /dev/urandom		#urandom提取的较快
tr命令可对数据进行替换,删除等操作
	-c:取反		-d:删除
[root@ztj test03]# tr -cd "0-9a-zA-Z"  <  /dev/urandom | head  -c 10

进度条脚本(如拷贝文件的脚本):

先定义一个函数,函数里面是一个死循环;给空格加个底色,调用函数放到后台,是脚本继续执行后面的命令;$!代表整个脚本中,最后一个后台进程的进程号

[root@ztj test03]# vim progress.sh
#!/bin/bash
bar(){
while :
do
        echo -en "\033[42m \033[0m"
        sleep 0.5
        done
}
bar &
cp -r $1 $2
kill $!
echo

fork炸弹

无限循环自己,消耗系统资源

[root@ztj test03]# .(){.|.&};.

.(){		#定义函数,函数为.
.|.&		#在函数内调用自己,并放后台
}
.		#执行函数

八、正则表达式

基本正则的使用

[root@ztj ~]# grep root /etc/passwd			#查找包含root的行
[root@ztj ~]# grep ^root /etc/passwd			#查找以root开头的行
[root@ztj ~]# grep bash$ /etc/passwd		#查找以bash结尾的行
[root@ztj ~]# grep "[abc]" /etc/passwd		#查找包含a或者b或者c的行
[root@ztj ~]# grep "[^abc]" /etc/passwd		#查找不包含a或者b或者c的其他内容
[root@ztj ~]# grep . /etc/passwd				#查找任意单个字符
[root@ztj ~]# grep r.*t /etc/passwd			#查找以r开头以t结尾的
[root@ztj ~]# grep "[0-9]*"  /etc/passwd		#查找包含数字的,*代表任意次
[root@ztj ~]# grep "[0-9]\{3,4\}” /etc/passwd	#查找包含数字3-4次的
[root@ztj ~]# grep "[0-9]\{3\}" /etc/passwd	#查找包含3位数的

扩展正则的使用

[root@ztj ~]# grep -E "0{2,3}" /etc/passwd			#查找0出现2-3次
[root@ztj ~]# grep -E "[a-z]+"  /etc/passwd		#查找a-z等字母至少出现一次
[root@ztj ~]# grep -E "s?bin"  /etc/passwd		#查找sbin或者bin(?匹配前面的s字符0-1次)
[root@ztj ~]# grep -E "(root|daemon)" /etc/passwd		#查找root或者daemon
[root@ztj ~]# echo "ababab" | grep ab 				#查找ab
ababab
[root@ztj ~]# echo "ababab" | grep -E "(ab)"			#查找ab
[root@ztj ~]# echo "ababab" | grep -E "(ab){2}"			#将ab组合,匹配两次

Perl兼容正则的使用

[root@ztj ~]# grep -P "bin" /etc/passwd    #匹配包含bin的行,只要包含bin字符的都出现
[root@ztj ~]# grep -P "\bbin\b" /etc/passwd	# \b单词边界,b前面不能有内容,n后面也不能有内容,只匹配bin
[root@ztj ~]# grep -P "\w"  /etc/passwd				#查找字母数字下划线
[root@ztj ~]#  grep -P "\W"  /etc/passwd			#查找不是字母数字下划线部分
[root@ztj ~]# grep -P "\s"  /etc/passwd				#查找空白,空格,tab键都算
[root@ztj ~]# grep -P "\d"  /etc/passwd				#查找数字
[root@ztj ~]# grep -P "\D"  /etc/passwd				#查找非数字

grep语法格式

用法: grep [选项] 匹配模式 [文件]…
常用选项:

-i		忽略大小写
-V		取反匹配
-W		匹配单词,和正则里面的\b是一样的
-q		静默匹配,不将结果显示在屏幕(无论结果是否匹配成功)

九、sed基础

什么是sed?
sed非交互式修改文档,逐行处理,可以对文本进行增删改查等操作
sed语法格式
语法:命令 | sed 选项 (定位符)指令
sed 选项 (定位符)指令 文件名 #(定位符)指令 想对文件的哪一行进行操作
选项

-n 屏蔽默认输出
-r 支持扩展正则
-i 写入文件

数据定位

定位符:行号定位
sed可以使用行号来定位自己需要修改的数据内容

[root@ztj test04]# sed "2p" /etc/hosts		#不加-n选项,默认全文打印,第二行打印了两边,因为有一遍是人为要求打印的
[root@ztj test04]# sed -n "2p" /etc/hosts		# -n 屏蔽默认输出,打印第二行内容
[root@ztj test04]# sed -n "3p" /etc/passwd	#打印第三行
[root@ztj test04]# sed -n "1,3p" /etc/passwd	#打印一到三行
[root@ztj test04]# sed -n "1~2p" /etc/passwd	#输出(奇数行)第1行开始步长为2
[root@ztj test04]# sed -n "2~2p" /etc/passwd	#输出(偶数行)第2行开始步长为2
[root@ztj test04]# sed -n "2,+3p" /etc/passwd 	#输出第2行,以及后面的3行

正则定位

sed可以使用正则匹配需要的数据,然后在编辑对应的内容,sed里面在使用正则是需要两个/ / 括起来
过滤root开头的行,使用sed打印

[root@ztj test04]# grep "^root" /etc/passwd
[root@ztj test04]# sed -n "/^root/p" /etc/passwd

过滤包含三个数字的行

[root@ztj test04]# grep  -E "[0-9]{3}" /etc/passwd
[root@ztj test04]# sed -rn "/[0-9]{3}/p" /etc/passwd		#使用扩展正则加r选项

使用sed修改配置

常用sed指令:

p(print):打印行
d(delete):删除行
c(replace):替换行
s(substitution):替换关键词
=:打印行号

过滤数据(print指令)

[root@ztj test04]# sed -n "/IPADDR/p" /etc/sysconfig/network-scripts/ifcfg-eth0   #过滤网卡IP地址
[root@ztj test04]# free | sed -n "/Mem/p"				#过滤内存信息
[root@ztj test04]# df  -h  | sed -n "/\/$/p"			#过滤磁盘根分区信息
[root@ztj test04]# sed -n "1p;3p;6p"  /etc/passwd		#打印不连续的多行
[root@ztj test04]# sed -n '2!p'  /etc/passwd			#不打印第二行,其他都打印

删除数据(delete指令,不适用-i选项,源文件不会被修改)

[root@ztj test04]# sed 'd' /etc/hosts	     #删除/etc/hosts全文,没有定位条件匹配所有行
[root@ztj test04]# sed '1d' /etc/hosts						#删除第一行
[root@ztj test04]# cat /etc/fstab > /tmp/fstab
[root@ztj test04]# sed '1,3d' /tmp/fstab					#删除1到3行
[root@ztj test04]# sed '/dev/!d' /tmp/fstab				#不包含dev的行都删除
[root@ztj test04]# sed '/^#/d' /tmp/fstab					#删除以#开头的行
[root@ztj test04]# sed '/^$/d' /tmp/fstab					#删除空行

使用i进行永久删除

[root@ztj test04]# sed -i  '1,4d' /tmp/fstab		#删除1-4行

替换行(c以行为单位)

[root@ztj test04]# sed  'c 123456' /tmp/fstab  			#将所有行替换为123456
[root@ztj test04]# sed  '/IPADDR/c IPADDR=1.1.1.1' /etc/sysconfig/network-scripts/ifcfg-eth0 
#查找IPADDR的行替换为IPADDR=1.1.1.1(替换IP地址)
[root@ztj test04]# sed  '/127/c 127.0.0.1 localhost' /etc/hosts	#查找127的行替换为127.0.0.1 localhost
[root@ztj test04]# sed '4c XXXX'  /etc/shells				#将/etc/shells文件的第四行替换为XXXX

替换关键词(格式: s/旧的/新的/)

[root@ztj test04]# vim test.txt
2046 2048 2046 2046
1001 2046 2999 1888
2046 2046 2046 2046
[root@ztj test04]# sed 's/2046/XXXX/' test.txt		#将每一行的第一个2046替换为XXXX
[root@ztj test04]# sed 's/2046/XXXX/g' test.txt 	#将每一行的所有2046替换为XXXX
[root@ztj test04]# sed 's/2046/XXXX/2' test.txt		#将每一行的第二个2046替换为XXXX
[root@ztj test04]# sed 's/2046/(&)/' test.txt		#将每一行的第一个2046替换为(2046) (&指替换的旧的内容,在这里指2046)
[root@ztj test04]# sed '2s/2046/XXXX/g' test.txt 	#将替换第二行的所有2046替换为XXXX
[root@ztj test04]# sed '2s/2046//g' test.txt		#替换为空,等同于删除
[root@svr5 ~]# sed -n '2s/2046/XXXX/p' test.txt		#n屏蔽输出,p打印显示。将第二行的2046替换为XXXX

将替换符改为其他字符,也可以达到同样的操作

[root@svr5 ~]# sed 's#2046#XXXX#g' test.txt		#以#为sed的指令替换符
[root@svr5 ~]# sed 's,2046,XXXX,g' test.txt			#以,为sed的指令替换符

正则符号()具有保留功能

[root@ztj test04]# echo "hello the world" | sed -r 's/^(.)(.*)(.)$/\3\2\1/'			
#扩展正则需要加r,.代表任意单个字符 \3\2\1代表第三个第二个第一个内容,\数字格式固定

打印行号 =

[root@ztj test04]# sed -n '1=' /etc/passwd			#打印第一行的行号
[root@ztj test04]# sed -n '/root/=' /etc/passwd		#打印包含root的行
[root@ztj test04]# sed -n '/bash$/=' /etc/passwd	#打印以bash结尾的行
[root@ztj test04]# sed -n '$=' /etc/passwd			#统计行数
[root@ztj test04]# wc -l /etc/passwd				#统计行数(会显示文件名)

sed多行文本处理

常用指令

i ( insert) :插入     (-i选项才会修改源文件)
a ( append) :追加
r (read) :读取文件|导入文件内容
w (write) :文件另存为|导出文件内容

插入指令:i ( insert)行前写入

[root@ztj test04]# sed '2i ABC_XYZ' test.txt			#在第二行前插入ABC_XYZ
[root@ztj test04]# sed '3i ABC_XYZ' test.txt			#在第三行前ABC_XYZ
[root@ztj test04]# sed '/2046/i ABC\nXYZ' test.txt		#在含有2046的行前面插入两行ABC和XYZ(\n换行回车)
[root@ztj test04]# sed '/1888/i ABC\nXYZ' test.txt		#在含有1888的行前面插入两行ABC和XYZ

追加指令:a ( append)行后写入

[root@ztj test04]# sed '2a ABC_ XYZ' test.txt		#在第二行后插入ABC_XYZ
[root@ztj test04]# sed '3a ABC_ XYZ' test.txt		#在第三行后ABC_XYZ
[root@localhost ~]# sed '/2046/a ABC\nXYZ' test.txt	#在含有2046的行后面插入两行ABC和XYZ(\n换行回车)
[root@localhost ~]# sed '/1888/a ABC\nXYZ' test.txt	#在含有1888的行后面插入两行ABC和XYZ

导入指令:r (read,将其他文件的内容导入)

[root@ztj test04]# sed '2r /etc/hosts' test.txt	#将/etc/hosts导入到test.txt的第二行后面
[root@ztj test04]# sed 'r /etc/hosts' test.txt	#将/etc/hosts导入到test.txt的每一行后面
[root@ztj test04]# sed '/1888/r /etc/hosts' test.txt	#将/etc/hosts导入到test.txt的含有1888的行后面

导出指令:w (write,将文件内容导出另存到其他文件)

[root@ztj test04]# sed 'w copy_test.txt' test.txt		#将test.txt文件的所有内容另存为一个新文件copy_ test.txt
[root@ztj test04]# sed '/1888/w 1888.txt' test.txt	#将test.txt文件中所有包含1888的行另存为新文件1888. txt
[root@ztj test04]# sed '2,3w line.txt' test.txt		#将test.txt文件的2到3行另存为新文件line.txt

十、awk基础语法

awk基础知识
作用:awk在shell脚本中主要是做数据分析和数据过滤

使用方法

命令 |  awk [选项][条件]{指令}#[条件]{指令}多条指令可以以分号分隔

awk 选项 ‘[条件]{指令}’ 文本
awk文本过滤的基本用法
格式:awk [选项] ‘[条件]{指令}’ 文件

直接过滤文件内容:

[root@ztj ~]# vim test.txt
hello the world
welcome to beijing 
[root@ztj ~]# awk '{print $1}' test.txt 	#打印第1列
hello
welcome
[root@ztj ~]# awk '{print $1,$3}' test.txt	#打印第1列和第3列
[root@ztj ~]# awk '{print $3,$1}' test.txt	#打印第3列和第1列

语法格式: -F 指定分隔符,默认分隔符为(空格或者tab键)

[root@ztj ~]# awk '{print $1}' /etc/passwd		#默认以空格或tab键打印
[root@ztj ~]# awk -F: '{print $1}' /etc/passwd	#以:为分隔打印

awk常用内置变量:

FS 保存或设置字段分隔符,例如FS=’:’,与-F功能一样
$0 文本当前行的全部内容
$1 文本的第1列
$2 文件的第2列
$3 文件的第3列,依此类推
NR 文件当前行的行数
NF 文件当前行的列数(有几列)
[root@ztj ~]# awk -F: '{print NF}' /etc/passwd		#以冒号为分隔,每行有几列
[root@ztj ~]# awk -F: '{print $NF}' /etc/passwd		#以冒号为分隔,每行有几列,$NF为这列的内容是什么
[root@ztj ~]# awk -F: '{print $(NF-1)}' /etc/passwd	#打印倒数第2列内容
[root@ztj ~]# awk '{print NR}' /etc/passwd			#打印行数

awk不仅可以打印变量,还可以打印常量

[root@ztj ~]# awk -F: '{print "用户名是:"$1,"UID是:"$3}' /etc/passwd

awk过滤的升级

awk会逐行处理文本,支持在处理第一行之前做一些准备工作,以及在处理完最后一行之后做一些总结性质的工作。在命令格式上分别体现如下:

awk  [选项]  '[条件]{指令}'  文件
awk  [选项]  ' BEGIN{指令} {指令} END{指令}'  文件
BEGIN{ } 行前处理,读取文件内容前执行,指令执行1次
{ } 逐行处理,读取文件过程中执行,指令执行n次
END{ } 行后处理,读取文件结束后执行,指令执行1次

awk做计算机

[root@ztj ~]# awk 'BEGIN{a=34;print a+12}'
[root@ztj ~]# awk 'BEGIN{a=34;b=33;print a+b}'
[root@ztj ~]# awk 'BEGIN{a=3.4;b=3.3;print a+b}'
awk统计/ect/passwd里面有多少个可以登录的用户
[root@ztj ~]# awk 'BEGIN{x=0} /bash$/{x++} END{print x}' /etc/passwd
输出打印之前的行数,以及读取文件之后的行数
[root@ztj ~]# awk -F: 'BEGIN {print NR} END{print NR}' /etc/passwd

awk条件判断

条件判断概述
条件表达式
正则表达式
数值/字符比较
逻辑比较
条件

使用正则设置条件

/正则表达式/ ~匹配 !~不匹配

[root@ztj ~]# awk -F: '/root/{print}' /etc/passwd		#打印包含root的行
[root@ztj ~]# awk -F: '$1~/root/{print}' /etc/passwd		#打印第一列是root的行
[root@ztj ~]# awk -F: '$7!~/bash$/{print}' /etc/passwd	#打印第7列不是bash结尾的行

使用数值/字符串比较设置条件

比较符号:==(等于) !=(不等于) >(大于) >=(大于等于) <(小于) <=(小于等于)

[root@ztj ~]# awk -F: 'NR==2' /etc/passwd					#打印第二行
[root@ztj ~]# awk -F: 'NR==2{print}' /etc/passwd			#打印第二行

字符串的比较

[root@ztj ~]# awk -F: '$1=="root"{print}' /etc/passwd		#打印第1列等于root的行
[root@ztj ~]# awk -F: '$1=="root"{print $1,$3}' /etc/passwd	#打印第1列等于root的字符串,显示第1列和第3列
[root@ztj ~]#  awk -F: '$3>=1000{print $1,$3}' /etc/passwd	#打印用户UID大于1000的用户名称和UID信息

逻辑比较 &&逻辑与:期望多个条件成立 ||逻辑或:只要一个条件成立既满足要求

[root@ztj ~]# awk -F: '$3>=0 && $3<2{print $1,$3}' /etc/passwd	#打印用户UID大于等于0并且小于2的用户信息
[root@ztj ~]# awk -F: '$3==1 || $3==7{print $1,$3}' /etc/passwd	#输出用户UID等于1或用户UID等于7的用户信息

运算符

[root@ztj ~]# awk 'NR%2==0' /etc/passwd		#打印偶数行,即NR行数对2取余得0
[root@ztj ~]# awk 'NR%2==1{print}' /etc/passwd	#打印奇数行,即NR行数对2取余得1
[root@ztj ~]# seq 200		#产生1-200之间的整数
判断这些数(1-200)能被3和13整除的数有多少个
[root@ztj ~]# seq 200 | awk 'BEGIN{i=0} $1%3==0 && $1%13==0{i++} END{print i}'	#满足条件执行i++,不满足执行下一个,最后打印i

awk流程控制

单分支if判断

单分支: awk ‘{ if() {指令} }’ 文件 #if(),()里面写条件,条件满足,执行{指令},不满足则不执行
统计/etc/passwd文件中UID大于等于1000的用户个数:

[root@ztj ~]# awk  -F: '{ if($3>=1000){i++} } END{print i}' /etc/passwd

统计/etc/passwd文件中用户名为root的用户名和uid:

[root@ztj ~]# awk -F: '{ if($1=="root"){print $1,$3} }' /etc/passwd

查看CPU负载,15分钟负载是否大于等于0.01,大于则输出CPU负载:

[root@ztj ~]# uptime | awk '{ if($NF>=0.01){print "CPU的负载为:"$NF} }'

双分支if判断

双分支: awk ‘{ if() {指令} else{} }’ 文件
统计/etc/passwd文件,普通用户(UID大于等于1000)和系统用户(小于1000)

[root@ztj ~]# awk -F: '{ if($3>=1000){i++}else{j++} } END{print "普通用户:"i, "系统用户:"j}' /etc/passwd

统计/etc目录有多少普通文件和目录

[root@ztj ~]# ls -l /etc/ | awk '{ if($1~/^-/){x++}else{y++} } END{print "普通文件:"x,"目录个数:"y}'

多分支if判断

多分支: awk ‘{ if() {指令} else if(){}… … else{} }’ 文件

[root@ztj ~]# ls -l /etc/| awk '{ if($1~/^-/){x++}else if($1~/^d/){y++}else{z++} } END{print "普通文件:"x,"目录个数:"y,"其他:",z}'

for循环

for循环格式: for (表达式1 ;表达式2;表达式3) {指令} #表达式1(初始值) ;表达式2(条件);表达式3(步长)

[root@ztj ~]# awk 'BEGIN{ for (i=1;i<=5;i++){print i}}' 	#屏幕打印12345
[root@ztj ~]# awk 'BEGIN{ for (i=5;i>=1;i--) {print i}}'		#屏幕打印54321

十一、Shell脚本综合练习案例

1、循环嵌套

打印星星矩阵

[root@ztj ~]# mkdir   /root/shell/test06
[root@ztj ~]# cd /root/shell/test06
[root@ztj test06]# vim star.sh	#输出一行5个星星
#!/bin/bash
for i in {1..5}
do
  echo -n "* "
done
[root@ztj test06]# chmod +x star.sh 
[root@ztj test06]# ./star.sh

[root@ztj test06]# vim star.sh	#打印星星矩阵
#!/bin/bash
for i in {1..5}
do
        for j in {1..5}
        do
          echo -n "* "
        done
        echo
done

排列与组合(打印1-3的所有排列组合方式)

[root@ztj test06]# vim compose.sh
#!/bin/bash
for i in {1..3}
do
        for j in {1..3}
        do
                echo ${i}${j}
        done
done
[root@ztj test06]# chmod +x compose.sh 
[root@ztj test06]# ./compose.sh

打印形状(打印正的阶梯色块)

[root@ztj test06]# vim sharp.sh
#!/bin/bash
for((i=1;i<=6;i++))
do
        for((j=1;j<=i;j++))
        do
                echo -ne "\033[42m  \033[0m"
        done
        echo
done
[root@ztj test06]# chmod +x sharp.sh 
[root@ztj test06]# ./sharp.sh

打印倒着的阶梯色块

[root@ztj test06]# vim sharp.sh
#!/bin/bash
for((i=1;i<=6;i++))
do
        for((j=6;j>=i;j--))
        do
                echo -ne "\033[42m  \033[0m"
        done
        echo
done
[root@ztj test06]# ./sharp.sh

将前面两个打印出来的色块拼接

[root@ztj test06]# vim sharp.sh
#!/bin/bash
for((i=1;i<=6;i++))
do
        for((j=1;j<=i;j++))
        do
                echo -ne "\033[42m  \033[0m"
        done
        echo
done

for((i=1;i<=5;i++))
do
        for((j=5;j>=i;j--))
        do
                echo -ne "\033[42m  \033[0m"
        done
        echo
done
[root@ztj test06]# ./sharp.sh

2、带菜单的脚本

带菜单的脚本

[root@ztj test06]# vim menu.sh
#!/bin/bash
echo "1.查看剩余内存容量."
echo "2.查看根分区剩余容量."
echo "3.查看CPU十五分钟负载."
echo "4.查看系统进程数量."
echo "5.查看系统账户数量."
echo "6.退出."
while :
do
  read -p "请输入选项[1-6]:" key
  case $key in
1)
  free | awk '/Mem/{print $NF}' ;;
2)
  df | awk '/\/$/{print $4}' ;;
3)
  uptime | awk '{print $NF}' ;;
4)
  ps aux | wc -l ;;
5)
  sed -n '$=' /etc/passwd ;;
6)
[root@ztj test06]# chmod +x menu.sh 
[root@ztj test06]# ./menu.sh

3、备份数据

备份日志

[root@ztj test06]# vim bak_log.sh
#!/bin/bash
date=`date +%Y%m%d`
if [ ! -f /tmp/log-$date.tar.gz ];then
   tar -czf /tmp/log-$date.tar.gz  /var/log/
fi
[root@ztj test06]# chmod +x bak_log.sh 
[root@ztj test06]# ./bak_log.sh

备份数据库,逻辑备份

[root@ztj test06]# yum -y install mariadb mariadb-server
[root@ztj test06]# systemctl restart mariadb
[root@ztj test06]# vim mysqldump.sh
#!/bin/bash
date=$(date +%Y%m%d)
iuser=root
ipass=
db=mysql

if [ ! -f /tmp/$db-$date.sql ];then
   mysqldump -u$iuser --password="$ipass" $db > /tmp/$db-$date.sql
fi
[root@ztj test06]# chmod +x mysqldump.sh 
[root@ztj test06]# ./mysqldump.sh

物理备份

[root@ztj test06]# vim bak_mysql.sh
#!/bin/bash
date=$(date +%Y%m%d)
db_dir="/var/lib/mysql"
db=mysql

[ ! -d /tmp/$db ] && mkdir /tmp/$db

for i in $(ls $db_dir/$db)
do
   tar -czf /tmp/$db/$i-$date.tar.gz  $db_dir/$db/$i
done
[root@ztj test06]# chmod  +x bak_mysql.sh 
[root@ztj test06]# ./bak_mysql.sh

差异备份(Inotify+sync)

[root@ztj test06]# vim insync.sh
#!/bin/bash
FROM_DIR="/var/www/html/"  	
RSYNC_CMD="rsync  -az  --delete  $FROM_DIR  root@192.168.4.207:/var/www/html" 
while  inotifywait  -rqq  -e  modify,move,create,delete,attrib  $FROM_DIR 
do
    $RSYNC_CMD
done  &
[root@ztj test06]# ./insync.sh

4、安全脚本

HSAH值
HSAH值与文件名称、时间、大小等信息无关,仅与内容有关
查看/etc/passwd文件HSAH值

[root@svr5 ~]# md5sum /etc/passwd			#文件内容不改变校验结果一样
[root@svr5 ~]# cp /etc/passwd  /root/pass		
[root@svr5 ~]# md5sum /root/pass			#校验结果和/etc/passwd检验结果一样
[root@svr5 ~]# vim /root/pass				#任意修改一行内容
[root@svr5 ~]# md5sum /root/pass			#校验结果和/etc/passwd检验结果不同	
[root@svr5 ~]# sha512sum /etc/passwd		#计算hash值的长度不同
[root@svr5 ~]# sha256sum /etc/passwd		

数据安全监测脚本

[root@ztj ~]# cd /root/shell/test06/
[root@ztj test06]# vim data.sh
#!/bin/bash
for i in $(ls /etc/*.conf)
do
   md5sum $i >> /tmp/data.log
done
[root@ztj test06]# chmod +x data.sh
[root@ztj test06]# ./data.sh

通过脚本修改SSH配置文件

sshd主配置文件: /etc/ssh/sshd_config
Port		3389			//改用非标准端口
PermitRootLogin no 		//禁止root登录
UseDNS no					//不解析客户机地址
AllowUsers 账户名			//设置远程连接的白名单,多个用户空格分割
[root@ztj test06]# cp /etc/ssh/sshd_config ./		#拷贝配置文件到当前目录
[root@ztj test06]# vim ssh_config.sh
#!/bin/bash
conf="./sshd_config"
sed -i '/^#Port/s/22/1122/' $conf
sed -i '/^#PermitRootLogin/s/yes/no/' $conf
sed -i '/^#UseDNS/s/yes/no/' $conf
sed -i '$a AllowUsers tom' $conf
systemctl restart sshd 
[root@ztj test06]# chmod +x ssh_config.sh 
[root@ztj test06]# ./ssh_config.sh

格式化输入passwd

[root@svr5 ~]# awk -F:  'BEGIN{print "用户名  UID  家目录"}' 
[root@svr5 ~]# awk -F:  'BEGIN{print "用户名  UID  家目录"}  {print $1 ,$3,$6}' /etc/passwd | column -t			# column -t 主要作用使用tab键缩进排版

过滤系统账户中对应的密码
在awk中可以通过-v选项调用shell中的变量

[root@svr5 ~]# hello="nihao"
[root@svr5 ~]# awk 'BEGIN{print hello}'			#无输出结果
[root@svr5 ~]# awk 'BEGIN{print "hello"}'			#打印字符串hello
[root@svr5 ~]# awk 'BEGIN{hello=123;print hello}'
123
[root@svr5 ~]# hello="nihao"
[root@svr5 ~]# awk -v x=$hello 'BEGIN{print x}'		
nihao

从/etc/passwd中将所有能登陆的账户名提取出来
从/etc/shadow中提取账户对应的密码

[root@ztj test06]# vim userpass.sh
#!/bin/bash
USER=$(awk -F: '/bash$/{print $1}' /etc/passwd)
for i in $USER
do
   awk -F: -v x=$i '$1==x{print $1,$2}' /etc/shadow
done	
[root@ztj test06]# chmod +x userpass.sh
[root@ztj test06]# ./userpass.sh

5、综合脚本

部署PXE+kicstart环境

DHCP服务,TFTP服务,HTTP服务,kickstart配置

[root@svr5 ~]# cat /root/shell/test06/pxe.sh 
#!/bin/bash
DHCP_NET=192.168.4.0
DHCP_NETMASK=255.255.255.0
DHCP_MINIP=192.168.4.100
DHCP_MAXIP=192.168.4.200
DHCP_ROUTER=192.168.4.1
DHCP_NEXT_SERVER=192.168.4.7
HTTP_IP=192.168.4.7

#安装软件包.

yum -y install httpd dhcp tftp-server syslinux
#临时停用SELinux
setenforce 0

#配置DHCP服务

cat > /etc/dhcp/dhcpd.conf << EOF
default-lease-time 600;
max-lease-time 7200;
subnet $DHCP_NET netmask $DHCP_NETMASK {
  range $DHCP_MINIP $DHCP_MAXIP;
  option routers $DHCP_ROUTER;
  next-server $DHCP_NEXT_SERVER;
  filename "pxelinux.0";
}
EOF
systemctl start dhcpd
systemctl enable dhcpd

#配置httpd共享服务器.

if [ ! -e /dev/cdrom ];then
   echo "未检测到系统光盘/dev/cdrom,请插入光盘后再试."
   exit
fi
[ -d /var/www/html/cdrom ] || mkdir /var/www/html/cdrom
mount /dev/cdrom /var/www/html/cdrom
systemctl start httpd

#配置kickstart文件.

cat > /var/www/html/ks.cfg << EOF
install
keyboard 'us'
rootpw --plaintext redhat
url --url="http://$HTTP_IP/cdrom"
lang en_US
firewall --disabled
auth  --useshadow  --passalgo=sha512
text
selinux --disabled
skipx
network  --bootproto=dhcp --device=eth0
reboot
timezone Asia/Shanghai
bootloader --location=mbr
zerombr
clearpart --all --initlabel
part /boot --fstype="xfs" --size=500
part / --fstype="xfs" --grow --size=1
%packages
@minimal
@core
%end
EOF

#配置tftp服务器.

cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/
cp /var/www/html/cdrom/isolinux/* /var/lib/tftpboot/
[ -d /var/lib/tftpboot/pxelinux.cfg ] || mkdir /var/lib/tftpboot/pxelinux.cfg/
cat > /var/lib/tftpboot/pxelinux.cfg/default <<EOF
default vesamenu.c32
timeout 600

label linux
  menu label ^Install CentOS 7
  kernel vmlinuz
  append initrd=initrd.img ks=http://$HTTP_IP/ks.cfg
EOF
systemctl start tftp
iptables -F

END…


shell学习总结:

提示:以上为本人学习shell的过程笔记,学会以上技能基本上可以胜任80%场景的shell自动化需求;千里之行始于足下,共勉!

学习进度小打卡:

  • SHELL基础
  • shell脚本的设计与运行
  • 变量的扩展运用
  • shell中的运算
  • 条件测试
  • shell函数
  • 字符处理与变换初始化
  • 正则表达式与grep语法格式
  • sed基础
  • awk基础语法
  • 综合案例练习
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值