shell编程

SHELL编程

学习书本来自"linux命令行与shell脚本编程大全·第三版"

———在此,Christine还想感谢她的先生Timothy,感谢他的鼓励、耐心和倾听,即使他并不理解她说的是什么,也能全心支持她。 --作者寄语

也有老男孩教育的一些笔记(正则,三剑客)

本笔记只做示例(pdf标记效率高),不记理论!

笔记格式“python缩进”


理解shell

(pwd;echo $BASH_SUBSHELL)&  #echo $BASH_SUBSHELL 检查是否生成了子shell,未生成输出0,生成将输出1或以上数字
	#括号的加入使命令列表变成了进程列表,生成了一个子shell来执行对应的命令。&放入后台执行
	
协程
coproc #就是将命令放入后台执行与&一样
	[root@localhost ~]# coproc sleep 50
	[1] 4257
	[root@localhost ~]# jobs
	[1]+  运行中               coproc COPROC sleep 50 & #协程默认给进程起了名字COPROC
	[root@localhost ~]# coproc ybt { sleep 50; } #协程自定义进程名字 coproc 自定义 { 命令; }(命令两边的空格必须有)
	[2] 4258
	[root@localhost ~]# jobs
	[1]-  运行中               coproc COPROC sleep 50 &
	[2]+  运行中               coproc ybt { sleep 50; } &

sh +x test.sh	#来调试脚本

环境变量

printenv或env #查看全局环境变量
set #显示局部变量、全局变量以及用户定义变量。
	[root@localhost ansible]# printenv HOME  
	/root
	[root@localhost ansible]# echo $HOME
	/root
	[root@ybt ~]# set |head -2
	BASH=/usr/bin/bash

局部变量

定义局部变量
	#局部变量尽量使用小写,系统环境变量都是大写
	#变量名、等号和值之间不能有空格,
	#局部变量,只能在当前shell使用,进入子进程和进入父进程都无法使用
	[root@localhost ~]# l=ybt
	[root@localhost ~]# echo $l
	ybt
定义全局变量	#export
	[root@localhost ~]# bash
	[root@localhost ~]# echo $l	#在子进程shell中无法使用父进程的局部变量
	
	[root@localhost ~]# exit
	[root@localhost ~]# echo $l	 #退出后继续使用
	ybt
	[root@localhost ~]# export l	#export将局部变量导出到环境变量,变量名不需要加$
	[root@localhost ~]# bash 
	[root@localhost ~]# echo $l	 #子进程可以使用了
	ybt
子shell中修改父shell全局变量的值,不影响父shell变量的值
	[root@localhost ~]# l=kexiao	#子shell中修改
	[root@localhost ~]# echo $l
	kexiao
	[root@localhost ~]# exit	
	[root@localhost ~]# echo $l		#值不变
	ybt
子shell中就算使用export修改父shell全局变量的值,也不影响父shell变量的值
	root@localhost ~]# bash
	[root@localhost ~]# l=kexiao	#修改
	[root@localhost ~]# export l	#将局部变量导出到环境变量
	[root@localhost ~]# echo $l
	kexiao
	[root@localhost ~]# exit
	[root@localhost ~]# echo $l	#值不变
	ybt
删除环境变量	#unset
	#在子进程删除全局变量只对子进程有效,与修改一样
	[root@localhost ~]# unset l
	
#要用到变量,使用$;如果要操作变量,不使用$  #printenv例外

变量每次被引用时,都会输出当前赋给它的值。引用一个变量值时需要使用美元符
#以下引用变量值可能导致的错误
	[root@ybt ~]# ke=10
	[root@ybt ~]# echo $ke
	10
	[root@ybt ~]# ti=ke+1	
	[root@ybt ~]# echo $ti
	ke+1					#这可不是我们想要的结果
	[root@ybt ~]# ti=$ke+1	#引用变量值需要使用$
	[root@ybt ~]# echo $ti
	10+1					#已经接近了我们想要的结果
	[root@ybt ~]# ti=$[ $ke+1  ]
	[root@ybt ~]# echo $ti	 
	11						#我们想要的结果

变量赋值拼接
	[root@ybt ~]# echo $l
	ybt
	[root@ybt ~]# kk=$l"kexiao"  #向已有变量拼接一个值
	[root@ybt ~]# echo $kk
	ybtkexiao

常用变量

#个人认为能用到的变量
HOME 当前用户的主目录
PATH shell查找命令的目录列表,由冒号分隔
PS1 shell命令行界面的主提示符
PS2 shell命令行界面的次提示符
BASH 当前shell实例的全路径名/ SHELL bash shell的全路径名
HOSTNAME 当前主机的名称
LANG shell的语言环境类别
PPID bash shell父进程的PID
PWD 当前工作目录
USER 显示当前用户
RANDOM 返回一个0~32767的随机数(对其的赋值可作为随机数生成器的种子)
SECONDS 自从shell启动到现在的秒数(对其赋值将会重置计数器)
$$ 当前shell的PID;脚本使用就是当前脚本pid
	[root@ybt ~]# echo $$
	77752


转义字符

\b	退格字符
\f	走纸符
\n	换行符
\r	回车符
\t	水平制表符
\v	垂直制表符
\ddd 1-3位八进制值
\xhex	十六进制值
\c	任何字面字符


关于linux特殊含义的转义符\033  #来自https://www.cnblogs.com/demonxian3/p/8963807.html
格式: echo -e "\033[字背景颜色;字体颜色m字符串\033[0m" 

如: 
echo -e "\033[41;36m something here \033[0m" 

其中41的位置代表底色, 36的位置是代表字的颜色 

那些ascii code 是对颜色调用的始末. 
\033[XX; m …… \033[0m   #XX表示下面的数字

字颜色:30-----------37
30:黑 
31:红 
32:绿 
33:黄 
34:蓝色 
35:紫色 
36:深绿 
37:白色 

字背景颜色范围:40----47
40:黑 
41:深红 
42:绿 
43:黄色 
44:蓝色 
45:紫色 
46:深绿 
47:白色

字体加亮颜色:90------------97
90:黑 
91:红 
92:绿 
93:黄 
94:蓝色 
95:紫色 
96:深绿 
97:白色

背景加亮颜色范围:100--------------------107
40:黑 
41:深红 
42:绿 
43:黄色 
44:蓝色 
45:紫色 
46:深绿 
47:白色

===============================================ANSI控制码的说明 
\33[0m 关闭所有属性 
\33[1m 设置高亮度 
\33[4m 下划线 
\33[5m 闪烁 
\33[7m 反显 
\33[8m 消隐 
\33[30m -- \33[37m 设置前景色 
\33[40m -- \33[47m 设置背景色 
\33[nA 光标上移n行 
\33[nB 光标下移n行 
\33[nC 光标右移n行 
\33[nD 光标左移n行 
\33[y;xH设置光标位置 
\33[2J 清屏 
\33[K 清除从光标到行尾的内容 
\33[s 保存光标位置 
\33[u 恢复光标位置 
\33[?25l 隐藏光标 
\33[?25h 显示光标


PATH

设置PATH环境变量
	#PATH定义查找命令和程序的目录
	[root@localhost ~]# echo $PATH	#查看现有路径
	/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
	[root@localhost ~]# which ps
	/usr/bin/ps
	[root@localhost ~]# mv /usr/bin/ps /home/ybt/scripts/	#将ps命令移走
	[root@localhost ~]# ps	#在执行系统已经找不到了
	-bash: ps: 未找到命令
	[root@localhost ~]# PATH=$PATH:/home/ybt/scripts/	#将移动到的路径导出到PATH环境变量中
	[root@localhost ~]# echo $PATH
	/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/home/ybt/scripts/
	[root@localhost ~]# ps	#命令又可以执行了
	   PID TTY          TIME CMD
	  2505 pts/1    00:00:00 bash
	  2660 pts/1    00:00:00 ps
	  
	重新将ps移回去
	[root@localhost ~]# cp  /home/ybt/scripts/ps /usr/bin/
	[root@localhost ~]# ps	#移动之后系统还是找不到
	-bash: /home/ybt/scripts/ps: 没有那个文件或目录
	[root@localhost ~]# cd /usr/bin/
	[root@localhost bin]# PATH=$PATH:.	#将移动到的路径导出到PATH环境变量中
	[root@localhost ~]# ps
  	 PID TTY          TIME CMD
  	2505 pts/1    00:00:00 bash
  	2720 pts/1    00:00:00 ps
#以上设置变量都是临时的,只能持续到退出或者重启系统

登录shell

​ 当你登录Linux系统时,bash shell会作为登录shell启动 p114

#登录shell会从5个不同的启动文件里读取命令
# /etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件。
#剩余4个文件是针对用户的,提供一个用户专属的启动文件来定义该用户所用到的环境变量。
	#它们位于用户的HOME目录下,所以每个用户都可以编辑这些文件并添加自己的环境变量,这些环境变量会在每次启动bash shell会话时生效。
/etc/profile
#$HOME/.bash_login	#CentOs是.bash_logout
$HOME/.bash_profile
$HOME/.bashrc
#$HOME/.profile 	#CentOs没有

/etc/profile文件都用到了同一个特性:for语句,它用来迭代/etc/profile.d目录下的所有文件
	/etc/profile.d 下大部分应用都会创建两个启动文件:一个供bash shell使用(使用.sh扩展名),一个供c shell使用(使用.csh扩展名)
	/etc/profile.d目录中创建一个以.sh结尾的文件。把所有新的或修改过的全局环境变量设置放在这个文件中。#不会因为升级系统而导致/etc/profile文件跟着更新,不过这种情况在生产环境不好出现!写到/etc/profile就行

存储个人用户永久性bash shell变量的地方是$HOME/.bashrc文件 #!!这个更实用
	它查看的还是/etc/bashrc里边配置的变量	#所以变量放这里边就可以了

交互式shell

不是登录时启动的shell,而是命令行输入bash启动的shell

如果bash是作为交互式shell启动的,它就不会访问/etc/profile文件,只会检查用户HOME目录中的.bashrc文件

非互交式shell

#执行脚本用的shell
	shell脚本是通过启动一个子shell来执行的。子shell可以继承父shell导出过的环境变量。
	由父shell设置但并未导出的变量都是局部变量。子shell无法继承局部变量。

数组变量

#数组是能够存储多个值的变量		
[root@localhost ~]# ybt=(wo shi zhan shen)	#括号创建,空格分隔,起始索引0
[root@localhost ~]# echo $ybt		#默认索引0
wo
[root@localhost ~]# echo ${ybt[2]}
zhan
[root@localhost ~]# echo ${ybt[3]}
shen
[root@localhost ~]# echo ${ybt[*]}	#查看全部
wo shi zhan shen
[root@ybt ~]# ybt[2]=fei	#指定索引修改
[root@ybt ~]# echo ${ybt[*]}
wo shi fei shen
[root@localhost ~]# unset ybt[2]	#删除索引为2的值,删除后,索引为2的值变为空
[root@localhost ~]# echo ${ybt[*]}
wo shi shen
[root@localhost ~]# unset ybt		
[root@localhost ~]# echo $ybt





创建脚本

#!/bin/bash 	#感叹号告诉shell用哪个shell来运行脚本,
	#shell不会解释以#开头的行(除了以#!开头的第一行)
[root@ybt ~]# cat joben.sh 	
echo 'ybt'	
echo "ybt"
[root@ybt ~]# ./joben.sh  #所有引号都正常输出
ybt
ybt

#""双引号	和单引号功能一样,但对特殊符号会做解析(识别不了通配符)
#''单引号	所见即所得
#没有引号	和双引号功能类似,但是可以直接识别通配符信息

不换行效果
[root@ybt ~]# cat joben.sh 
#!/bin/bash
echo -n "The time and date are: "	 -n不换行
date
[root@ybt ~]# sh joben.sh 
The time and date are: 2022年 03月 19日 星期六 02:10:18 CST

显示环境变量信息
[root@ybt ~]# cat joben.sh 
#!/bin/bash
echo $HOME
echo $l		#自定义的环境变量,不想输出变量\输入转义字符
[root@ybt ~]# sh joben.sh 
/root
ybt

用户变量
#用户变量可以是任何由字母、数字或下划线组成的文本字符串,长度不超过20个。用户变量区分大小写
[root@ybt ~]# cat joben.sh 
#!/bin/bash
ke="hello world"	#定义用户变量
echo kexiao $ke		#引用变量适用$
[root@ybt ~]# ./joben.sh 
kexiao hello world

命令替换:提取命令输出信息``或$()
	#可以从命令输出中提取信息,并将其赋给变量。
[root@ybt ~]# cat joben.sh 
#!/bin/bash
ybt=$(date +%y%m%d)	#使用$()
ke=`pwd`	#使用``
touch ybt.$ybt	
echo $ke
echo `ls ybt*`	#进阶使用
[root@ybt ~]# ./joben.sh 
/root
ybt.220319

数学运算

[root@ybt ~]# expr 1 + 2 #expr命令
3
[root@ybt ~]# echo $((10*10)) #$(())
100
[root@ybt ~]# echo $[10*10]	 #$[]
100
#他们都无法计算小数
	[root@ybt ~]# expr 10 * 10.1
	expr: 语法错误
	[root@ybt ~]# echo $((10*10.1))
	-bash: 10*10.1: 语法错误: 无效的算术运算符 (错误符号是 ".1")
	[root@ybt ~]# echo $[10*10.1]
	-bash: 10*10.1: 语法错误: 无效的算术运算符 (错误符号是 ".1")


#使用bc计算器 -q使去掉登录欢迎 ,scale变量的默认值是0,bash计算不包含小数位,scale来确定小数位
	[root@ybt ~]# bc -q		#命令行使用 bc计算器 yum -y install bc
	10*10.1
	101.0
	ybt=10	#可以变量赋值
	print ybt	#输出变量
	10
	quit  #退出命令
	
bc脚本示例
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	echo 小数计算 `echo "scale=7; 3.1415126 * 5" | bc`		#设置保留7位小数
	[root@ybt ~]# ./ybt.sh 	#执行脚本
	小数计算 15.7075630		

多重运算使用EOF文本重定向
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	ke=`bc << EOF
	a= 10 * 5	#EOF文本bc中赋值没有那么严格,shell不会识别成命令
	b= 100 * 5
	a + b
	EOF
	`
	echo $ke
	
	[root@ybt ~]# ./ybt.sh 	#执行脚本
	550

退出状态码

[root@ybt ~]# echo $?	#执行完脚本可以输入检查退出状态码
0
[root@ybt ~]# kk
-bash: kk: 未找到命令
[root@ybt ~]# echo $?
127

退出状态码参考
	0 命令成功结束
	1 一般性未知错误		#一般是无效参数
	2 不适合的shell命令
	126 命令不可执行	#没有执行权限
	127 没找到命令
	128 无效的退出参数
	128+x 与Linux信号x相关的严重错误
	130 通过Ctrl+C终止的命令
	255 正常范围之外的退出状态码

exit指定退出状态码
	#默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	echo ybt
	exit 10		#指定退出状态码
	[root@ybt ~]# sh ybt.sh 
	ybt
	[root@ybt ~]# echo $?
	10


if-then语句

语法格式
	if command
	then 
	 commands
	fi

脚本示例
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	if pwd	#pwd执行成功,退出码为0,正常执行then部分命令
	then
		echo "语法不如python"	#不贬低任何一门语言!因为它们都是伟大的!
	fi
	
	if python1;then	#写成一行
		echo "没有pyhton1命令,退出码非0,不会执行此条命令"		#then部分可以写多条命令
		echo '多条命令示例'
		echo '多条'
		echo '条'
	fi
	
	[root@ybt ~]# ./ybt.sh 	#执行脚本
	/root
	语法不如python
	./ybt.sh:行7: python1: 未找到命令	#没有python1,退出码非0,所有then部分的命令不在执行

if-then-else 语句

语法格式
	if command	
		#if命令执行成功 ,退出码为0,将执行then部分,不执行else;
		#if命令执行失败,退出码非0,不执行then部分,执行eles部分命令
	then 
	 commands		
	else 
	 commands
	fi
脚本示例
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	if pwd
	then
		echo "语法不如python"
	else
		echo 'then部分执行成功,else不执行'
	fi
	
	if python1;then
		echo "没有pyhton1命令,退出码非0,不会执行此条命令"
		echo '多条命令示例'
		echo '多条'
		echo '条'
	else
		echo 'then部分未执行执行else命令'
		echo '多条命令示例'
		echo '多条'
	fi
	
	[root@ybt ~]# ./ybt.sh 	#执行脚本
	/root
	语法不如python
	./ybt.sh:行9: python1: 未找到命令
	then部分未执行执行else命令
	多条命令示例
	多条
	

嵌套 if

语法格式
	if command1
	then 
	 commands
	elif command2	#if非0退出码非0,将执行elif,elif退出码非0,将执行elif部分的else;实现套娃
	then 
	 more commands
	else
		commands
	fi

脚本示例
#这是一个繁琐不易读的if嵌套示例
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	if ls /root/ybt
	then 
		echo '没有,不执行此命令'
	else
		echo '执行else部分'		#shell子代码不用缩进,我只是习惯,看起来也易读
		if ls /root/ybt.sh
		then
			echo '我会输出'
	 	fi
	fi
	
	
	[root@ybt ~]# ./ybt.sh #脚本执行
	ls: 无法访问/root/ybt: 没有那个文件或目录
	执行else部分
	/root/ybt.sh
	我会输出

if-then-elif...嵌套示例
	#elif嵌套已于阅读
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	if
	grep 29 python.py
	echo ybt	#此命令可以执行,只要最后一条命令退出码非0将不在执行then
    grep 29 python.py		#grep查找不到,退出码不为0
	then 
		echo '没有,不执行此命令'
	elif
		echo '执行elif部分'		
	 	ls /root/ybt
	then
		echo '输出失败'
	else			#这里还可以写elif实现无线嵌套
		echo '输出成功'
	fi

	[root@ybt ~]# ./ybt.sh 	#执行脚本
	ybt
	执行elif部分
	ls: 无法访问/root/ybt: 没有那个文件或目录
	输出成功
#从以上例子可以写成无线嵌套

test

语法格式
if test conditions #conditions是测试条件
	#test判断成立退出码返回0,否则返回非0

脚本测试
	变量判断测试
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	ybt=""	
	ke="hello"
	if test "$ybt"	#判断ybt变量为空,退出码非零 #判断变量最好加上双引号,以免发生意外错误
	then 
		echo "1<2"
	elif
		test $ke	#判断ke变量不为空,退出码0
		then
			echo $ke
	fi

	[root@ybt ~]# ./ybt.sh 执行脚本
	hello

无需声明test即可判断条件[]号代替
语法格式
	if [ condition ] #[空格条件空格],条件两边没有空格直接报错!
脚本测试
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	ybt=""
	ke="hello"
	if [ "$ybt" ]	#判断变量最好加上双引号,以免发生意外错误
	then 
		echo "1<2"
	elif [ "$ke" ]
		then
			echo $ke
	fi
	
	[root@ybt ~]# ./ybt.sh 	执行脚本
	hello

test判断

test命令可以判断三类条件:

数值比较
数值比较
	#对数值使用数学运算符号,shell会将它们当成字符串值,可能无法得到正确的结果。
	#bash shell只能处理整数
	n1 -eq n2 检查n1是否与n2相等
	n1 -ge n2 检查n1是否大于或等于n2ge
	n1 -gt n2 检查n1是否大于n2
	n1 -le n2 检查n1是否小于或等于n2
	n1 -lt n2 检查n1是否小于n2
	n1 -ne n2 检查n1是否不等于n2
	
脚本示例
	[root@ybt ~]# cat ybt.sh 
	#!/bin/bash
	if echo "判断100>200" 
	[ 100 -ge 200  ]; then
		echo 100 >=200
	elif echo "判断100=100"
	[ 100 -eq 100  ];then
		echo 结果为:100 = 100
	fi
	
	[root@ybt ~]# ./ybt.sh 执行脚本
	判断100>200
	判断100=100
	结果为:100 = 100

字符串比较
字符串比较	
	#大于号和小于号必须转义;
	#比较测试中使用的是标准的ASCII顺序,根据每个字符的ASCII数值来决定排序结果
	#sort排序是按英语当地字母排序,英语本地化设置指定了在排序顺序中小写字母出现在大写字母前。
	tr1 = str2 检查str1是否和str2相同
	str1 != str2 检查str1是否和str2不同
	str1 < str2 检查str1是否比str2小
	str1 > str2 检查str1是否比str2大
	-n str1 检查str1的长度是否非0 	#不为0继续,为0退出
	-z str1 检查str1的长度是否为0	#为0继续,不为0退出
	
脚本示例
	[root@ybt ~]# cat str.sh 
	#!/bin/bash
	if echo "判断ybt = ke" 
		[ ybt \= ke  ];then		#符号判断都用到了转义符号
		echo "ybt = ke"
	elif echo "判断ybt > ke" 
		[ ybt \> ke  ];then
		echo "ybt > ke"
	elif echo "判断ybt < ke" 
		[ ybt \< ke ];then
		echo "ybt <ke"
	else	echo "ybt不小于ke"
	fi
	
	[root@ybt ~]# ./str.sh	执行脚本
	判断ybt = ke
	判断ybt > ke
	ybt > ke

	


文件比较
文件比较
	-d file 检查file是否存在并是一个目录
	-e file 检查file是否存在 #可用于文件和目录
	-f file 检查file是否存在并是一个文件
	-r file 检查file是否存在并可读
	-s file 检查file是否存在并非空
	-w file 检查file是否存在并可写
	-x file 检查file是否存在并可执行
	-O file 检查file是否存在并属当前用户所有
	-o or或者 如:[  -f file -o -d file1 ]
	-G file 检查file是否存在并且默认组与当前用户相同
	file1 -nt file2 检查file1是否比file2新
	file1 -ot file2 检查file1是否比file2旧
脚本示例
	[root@ybt ~]# cat file.sh 
	#!/bin/bash
	if [ -f /home/ybt  ];then
		echo "ybt不是文件"
	else [ -e /home/ybt  ]		
		echo "ybt是目录"
	fi
	if [ -f /root/str.sh  ];then	
		echo "str.sh 脚本存在"
	fi

	
	[root@ybt ~]# ./file.sh 执行脚本
	ybt是目录
	str.sh 脚本存在

演示2:
	[root@ybt ~]# cat file.sh 
	#!/bin/bash
	if [ -d $HOME/str.sh  ];then
		echo "str.sh是一个目录"
	else [ -f $HOME/str.sh  ]
		echo "str.sh是文件"
		if [ -s  $HOME/str.sh  ];then
			echo "str.sh不为空"
		else echo "str.sh为空"
		fi
	fi

	
	[root@ybt ~]# ./file.sh 	执行脚本
	str.sh是文件
	str.sh不为空


if-then高级特性

用于数学表达式的双括号

语法格式
	(( expression ))
双括号
	val++ 后增
	val-- 后减
	++val 先增
	--val 先减
	! 逻辑求反
	~ 位求反
	** 幂运算
	<< 左位移
	>> 右位移
	& 位布尔和
	| 位布尔或
	&& 逻辑和
	|| 逻辑或	#命令行或,在正则里是 |
脚本示例
	[root@ybt ~]# cat shuang.sh 
	#!/bin/bash
	ybt=100
	if (( ybt ** 2 >100 ));then		
		#可以判断,不需要转义了;还是无法判断浮点;没有空格要求,$符号也不用写了
		(( ke=ybt**2 ))		#可以赋值
		echo "输出:$ke"
	fi
	
	[root@ybt ~]# ./shuang.sh  脚本执行
	输出:10000
示例2
	[root@ybt exercise]# cat zeng.sh 
	#!/bin/bash
	ybt=10
	while [ 1 ];do
		echo $ybt
		if ((ybt==60));then
			break
		fi
		((ybt=ybt+10))
		done
	[root@ybt exercise]# ./zeng.sh 
	10
	20
	30
	40
	50
	60
示例3
	[root@ybt exercise]# cat zeng.sh 
	#!/bin/bash
	ybt=10
	while [ 1 ];do
		echo $ybt
		if ((ybt!=10 ));then
			break
		fi
		((ybt++))
		done
	[root@ybt exercise]# ./zeng.sh 
	10
	11

用于高级字符串处理功能的双方括号

语法格式
	[[ expression ]]
双方括号
	#双方括号里的expression使用了test命令中采用的标准字符串比较。提供模式匹配
脚本示例
	[root@ybt ~]# cat root.sh 
	#!/bin/bash
	if [[ $USER == r* ]];then	#双等号将右边的字符串(r*)视为一个模式,并应用模式匹配规则。有空格要求
		echo "hello $USER"
	else
		echo "你谁啊你"
	fi
	
	[root@ybt ~]# cat root.sh 	执行脚本
	hello root
	

case

查找单个变量多个值用if-then-elif写
	[root@ybt ~]# cat case.sh 
	#!/bin/bash
	ybt=5
	if [ $ybt = 1  ] || [ $ybt = 2  ] ||[ $ybt = 3  ]
	then
		echo '普通用户'
		echo '欢迎'
	elif [ $ybt = 5  ] ||[ $ybt = 6  ]
	then
		echo '管理员'
		echo '请做好分内工作'
	elif [ $ybt = 7  ]
	then
		echo "超级管理员"
	else
		echo "没有找到"
	fi

	[root@ybt ~]# ./case.sh 执行脚本
	管理员
	请做好分内工作

case命令会采用列表格式来检查单个变量的多个值
#case命令会将指定的变量与不同模式进行比较。如果变量和模式是匹配的,那么shell会执行为该模式指定的命令。
#可以通过竖线操作符在一行中分隔出多个模式模式。星号会捕获所有与已知模式不匹配的值。
语法格式
	case variable in 
	pattern1 | pattern2) commands1;; 
	pattern3) commands2;; 
	*) default commands;; 
	esac
脚本示例
	[root@ybt ~]# cat case.sh 
	#!/bin/bash
	ybt=(5 7)
	case $ybt in 
	1|2|3|4)
		echo '普通用户'
		echo '欢迎';;
	5|6)
		echo '管理员'
		echo '请做好分内工作';;
	7)
		echo "超级管理员";;
	*)
		echo "没有找到";;
	esac		#跟命令开头到过来了,...
	
	[root@ybt ~]# ./case.sh 执行脚本
	管理员
	请做好分内工作

for

#重复执行一系列命令
语法格式
	for var in list 
	do 
	 commands 
	done

命令行读取文件示例
	[root@ybt ~]# cat 29
	1
	2
	3
	ybt ke	
	[root@ybt ~]# for i in `cat 29`; do  echo $i; done 	
	1
	2
	3
	ybt
	ke
	#for把一行的ybt ke隔开了,造成这个问题的原因是特殊的环境变量IFS
	#IFS环境变量定义了bash shell用作字段分隔符的一系列字符
	#默认为:空格,制表符,换行符
脚本示例
	#!/bin/bash
	IFS=$'\n'		#设置以换行符为的分隔符号;指定多个IFS=$'\n':;"会将换行符、冒号、分号和双引号作为字段分隔符
	for i in `cat $HOME/29`
	do
	echo $i
	done
	
	[root@ybt ~]# ./for.sh 	#执行脚本;看到了我们想要的结果
	1
	2
	3
	ybt ke

系统默认空格分隔,但这不是我们想要的结果
	[root@ybt ~]# for i in kk kk 1 kk 2 kk 3
	> do
	> echo 这是:$i
	> done
	这是:kk
	这是:kk
	这是:1
	这是:kk
	这是:2
	这是:kk
	这是:3
	#加上双引号后正常
	[root@ybt ~]# for i in kk "kk 1" "kk 2" "kk 3"; do echo 这是:$i; done
	这是:kk
	这是:kk 1
	这是:kk 2
	这是:kk 3

下面是单引号造成的问题
	#解决方法使用双引号或者转义字符
	[root@ybt ~]# for i in kk'1 kk'2 
	> do
	> echo $i
	> done
	kk1 kk2
	#转义解决
	[root@ybt ~]# for i in kk\'1 kk\'2 ; do echo $i; done	
	kk'1
	kk'2
	#双引号解决
	[root@ybt ~]# for i in "kk'1" "kk'2"; do echo $i; done
	kk'1
	kk'2

	
for循环应用
	查找目录下的文件或目录
	[root@ybt ~]# cat for.sh 
	#!/bin/bash
	for i in $HOME/*	#匹配家目录下所有
	do
		if [ -f $i  ]
		then 
		echo "文件:$i"
		else
		echo "目录:$i"
		fi
	done
	
	因为我的家目录下只有文件和目录
	[root@ybt ~]# ./for.sh 	执行脚本
	文件:/root/29
	文件:/root/case.sh
	文件:/root/file.sh
	文件:/root/for.sh
	文件:/root/python.py
	文件:/root/root.sh
	文件:/root/shuang.sh
	文件:/root/str.sh
	文件:/root/ybt.sh
	目录:/root/ybt:

	

C风格for

语法格式
for (( variable assignment ; condition ; iteration process ))
	#起始值;条件;迭代过程(自增或自减)

命令行示例
	[root@ybt ~]# for ((i=1; i<5; i++)); do echo $i; done
	1
	2
	3
	4
可以定义两个变量,但只能执行一个条件
	[root@ybt ~]# for ((i=1, b=10; i<5; i++,b--)); do echo $i,$b; done
	1,10
	2,9
	3,8
	4,7
定义两个条件,只按照其中一个条件执行了
	[root@ybt ~]# for ((i=1, b=10; i<5,b>1; i++,b--)); do echo $i,$b; done
	1,10
	2,9
	3,8
	4,7
	5,6
	6,5
	7,4
	8,3
	9,2

while

#while定义一个的测试命令,循环执行一组命令,只要定义的测试命令返回的是退出状态码0。它会在每次迭一开始测试test命令。在test命令返回非零退出状态码时,while命令会停止执行那组命令
语法格式
	while test command 	
	do 
	 other commands 
	done
脚本示例
	[root@ybt ~]# cat while.sh 
	#!/bin/bash
	ybt=5
	while [ $ybt -ge 0  ]		#只有满足定义的条件退出码非0时,才会停止循环,否则会一直循环
		do
		echo $ybt
		ybt=$[ $ybt -1  ]
	done
	
	[root@ybt ~]# ./while.sh 执行脚本
	5
	4
	3
	2
	1
	0

示例2
	[root@ybt ~]# cat while.sh 
	#!/bin/bash
	ybt=3
	while	echo "条件输出:$ybt" 	#可以定义多个测试条件,但是需要换行
	[ $ybt -gt 0  ]
		do
		echo "命令输出:$ybt"
		ybt=$[ $ybt -1  ]
	done
	
	[root@ybt ~]# ./while.sh 
	条件输出:3
	命令输出:3
	条件输出:2
	命令输出:2
	条件输出:1
	命令输出:1
	条件输出:0		#条件输出的最后失败迭代也循环了出来,退出码非零也循环了一次
	
示例3
	[root@ybt ~]# cat while.sh 
	#!/bin/bash
	ybt=3
	ke=-3
	while	[ $ke -lt 0  ]	#无线循环条件如果与有限条件呼唤位置,测试命令就会一直返回退出码0,while程序会一直循环
	 [ $ybt -gt 0  ]	#有限循环条件,测试条件最后一条返回非零后,程序退出
		do
		echo "命令输出:$ke"
		echo "命令输出:$ybt"
		ybt=$[ $ybt -1  ]
		ke=$[ $ke -1  ]
	done
	
	[root@ybt ~]# ./while.sh 
	命令输出:-3
	命令输出:3
	命令输出:-4
	命令输出:2
	命令输出:-5
	命令输出:1
	

while [ 1 ] 无线循环,只要循环内容非空即可,也可以写成while [ ture ]等;或者直接写成冒号whiel :
	#单个字符或单词退出状态码都为0的原因吧
	[root@ybt ~]# [ 1 ]
	[root@ybt ~]# echo $?
	0
	[root@ybt ~]# [ y ]
	[root@ybt ~]# echo $?
	0
	[root@ybt ~]# [ ture ]
	[root@ybt ~]# echo $?
	0
	[root@ybt ~]# [ false ]
	[root@ybt ~]# echo $?
	0
#因为while只有测试命令退出码为非0才能结束循环,所以以上都变成了无线循环

until

#until命令和while命令工作的方式完全相反
语法格式
	until test commands #只有测试条件退出码为0时才退出
	do
	 other commands 
	done
脚本示例
	[root@ybt ~]# cat until.sh 
	#!/bin/bash
	ybt=10
	until [ $ybt -lt 0  ]	#因为10不小于0,所以退出码每次都是非零,知道最后0不小于0,程序退出
		do
		echo $ybt
		ybt=$[ $ybt-2 ]
	done
	
	[root@ybt ~]# ./until.sh 	执行脚本
	10
	8
	6
	4
	2
	0

	

嵌套循环

while嵌套循环示例
	[root@ybt ~]# cat while.sh 
	#!/bin/bash
	ybt=3
	while [ $ybt -ge  1  ]
	do
		echo hello $ybt
		ybt=$[ $ybt-1  ]
		for i in 1 2 3
		do 
		echo $i
		done
	done
	
	[root@ybt ~]# ./while.sh 执行脚本
	hello 3
	1
	2
	3
	hello 2
	1
	2
	3
	hello 1
	1
	2
	3

for嵌套循环示例
	[root@ybt ~]# cat for.sh 
	#!/bin/bash
	for ((i=1;i<=3;i++))
	do
		echo hello $i
		for ((s=1;s<=3;s++))
		do 
		echo $s
		done
	done
	
	[root@ybt ~]# ./for.sh 执行脚本
	hello 1
	1
	2
	3
	hello 2
	1
	2
	3
	hello 3
	1
	2
	3
	
until和while嵌套示例
	[root@ybt ~]# cat until.sh 
	#!/bin/bash
	y=3
	until [ $y -le 0  ]
	do
		echo hello $y
		k=3				#根据上面两个示例,内循环变量一定要写到内部,要不然写道外部,内部循环只会执行一次
		y=$[ $y-1  ]
		while [ $k -ge 1 ]
		do
		echo $k
		k=$[ $k-1  ]
		done
	done
	
	[root@ybt ~]# ./until.sh 执行脚本
	hello 3
	3
	2
	1
	hello 2
	3
	2
	1
	hello 1
	3
	2
	1
	
#规律:内部循环会在外部循环的每次迭代中遍历一次它所有的值

控制循环

break

break终止循环
break n #n是参数,指的是退出几层循环,默认1当前
脚本示例
	单层循环终
	[root@ybt ~]# cat break.sh 
	#!/bin/bash
	ybt=5
	while [ $ybt -ge 0  ]
	do
		echo $ybt
		if [ $ybt -eq 8 ];then	
		break
		fi
		ybt=$[ $ybt+1  ]
	done 
	
	[root@ybt ~]# ./break.sh 脚本执行
	5
	6
	7
	8

终止本次循环和外层循环
	[root@ybt ~]# cat ./break.sh 
	#!/bin/bash
	ke=10
	while [ $ke -ge 0  ]
	do
		ybt=5
		while [ $ybt -ge 0  ]
		do
			echo $ybt
			if [ $ybt -eq 2 ];then
			break 2		#终止了本层循环和外层循环
			fi
			ybt=$[ $ybt-1  ]
		done
	echo hello $ke
	ke=$[ $ke-1  ]
	done  > 29.txt		#可以直接将循环结果交给文件,不在命令行显示 
				#也可以使用|来执行命令;如done | sort排序
	
	[root@ybt ~]# ./break.sh 执行脚本
	[root@ybt ~]# cat 29.txt
	5
	4
	3
	2
	
	

continue

continue命令可以提前中止某次循环中的命令,但并不会完全终止整个循环。
	终止本次循环,开始下次循环
continue n #终止n层之后的循环,重新开始外n层的循环,默认1当前

脚本示例
	[root@ybt ~]# cat continue.sh 
	#!/bin/bash
	for ((ybt=1;ybt<10;ybt++))
	do
		if [ $ybt -gt 3  ] && [ $ybt -lt 6  ] 
		then
		continue
		fi
		echo $ybt	#别把输出命令写道continue前面,continue会失效!
	done
	[root@ybt ~]# ./continue.sh 脚本执行
	1
	3
	6
	8
	
嵌套循环
	[root@ybt ~]# cat continue.sh 
	#!/bin/bash
	for ((i=1;i<3;i++))
	do 
		for ((ybt=1;ybt<10;ybt++))
		do
		if [ $ybt -gt 3  ]
		then
		continue #到3后因为3不大于3所以终止后,重新执行;如果continue 2 到3时,外层循环也会被终止
		fi
		echo $ybt	
		done
	echo hello $i
	done
	
	[root@ybt ~]# ./continue.sh 执行脚本
	1
	2
	3
	hello 1
	1
	2
	3
	hello 2
	
#可以在while和until循环中使用continue命令,但要它可能会跳过剩余的命令。比如你把增值变量写到了continue下边,

脚本参数

#脚本参数:$0是程序名,$1是第一个参数,$2是第二个参数,依次类推,直到第九个参数$9。
	#默认9个参数,如果想添加,脚本中就需要这样写${10}
脚本示例
	#很方便
	[root@ybt ~]# cat canshu.sh 
	#!/bin/bash
	echo 第一行参数 :$1 $2 $3 $4
	echo 第二行参数 : $5 $6 $7 $8 $9		
	echo 第三行命令行参数 ${10} ${11}
	
	#脚本执行
	[root@ybt ~]# sh canshu.sh  ybt kexiao peiqi  zms rose 'kk ll' "gg pp" 888 999 100 111
	#参数默认是以空格分隔的,如果不想被分隔'kk ll' "gg pp" 单引号和双引号可以解决
	第一行参数 :ybt kexiao peiqi zms
	第二行参数 : rose kk ll gg pp 888 999
	第三行命令行参数 100 111
	
关于默认$0程序名
脚本示例
	[root@ybt ~]# cat canshu2.sh 
	#!/bin/bash
	echo $0
执行脚本
	[root@ybt ~]# ./canshu2.sh 	
	./canshu2.sh
	[root@ybt ~]# /root/canshu2.sh 
	/root/canshu2.sh
	[root@ybt ~]# sh canshu2.sh 
	canshu2.sh
#你用什么方式执行它就怎么呈现

basename命令会返回不包含路径的脚本名。
脚本示例
	[root@ybt ~]# cat canshu2.sh 
	#!/bin/bash
	echo $(basename $0)
	
	[root@ybt ~]# /root/canshu2.sh 脚本执行
	canshu2.sh
	[root@ybt ~]# sh canshu2.sh 
	canshu2.sh
	
如果脚本有默认参数变量,不加参数可能会报错
	[root@ybt ~]# cat ./canshu2.sh 
	#!/bin/bash
	if [ "$1" -eq 0  ];then
		echo 不报错
	else
		echo "报错了"
	fi
	
	[root@ybt ~]# ./canshu2.sh 		执行脚本不加参数
	./canshu2.sh: 第 2 行:[: : 期待整数表达式
	报错了
	[root@ybt ~]# ./canshu2.sh 0	执行脚本加参数
	不报错

测试参数

使用-n测试来检查命令行参数$1长度是否为非0。 #不为零继续
	[root@ybt ~]# cat ceshi.sh 
	#!/bin/bash
	if [ -n "$1" ];then
		echo '有值'
	else	
		echo "没有值"
	fi
	
	[root@ybt ~]# ./ceshi.sh 
	没有值
	[root@ybt ~]# ./ceshi.sh 2
	有值



参数统计

$#含有脚本运行时携带的命令行参数的个数。
	#当命令行上没有任何参数时,$#的值为0,
	[root@ybt ~]# cat canshu3.sh
	#!/bin/bash
	echo "显示参数个数:$#"
	
	[root@ybt ~]# sh canshu3.sh 1 2 3 5
	显示参数个数:4
	[root@ybt ~]# sh canshu3.sh 1 2 3
	显示参数个数:3
	[root@ybt ~]# sh canshu3.sh  -b 1 2 3
	显示参数个数:4
	[root@ybt ~]# sh canshu3.sh  
	显示参数个数:0

${!#}显示脚本命令行最后一个参数值,如果没有输入参数默认输出脚本名称;#不要为我为什么不是 ${$#}
	[root@ybt ~]# cat canshu3.sh 
	#!/bin/bash
	echo 参数个数: $# 
	echo 最后一个参数值 ${!#} 
	
	[root@ybt ~]# ./canshu3.sh 
	参数个数: 0
	最后一个参数值 ./canshu3.sh
	[root@ybt ~]# ./canshu3.sh ybt
	参数个数: 1
	最后一个参数值 ybt



if-then语句用-ne测试命令行参数数量。如果参数数量不对,会执行then部分,条件对执行else部分。
	[root@ybt ~]# cat ybt.sh
	#!/bin/bash
	if [ $# -ne 2 ];then
		echo
		echo 参数个数不对
		echo
	else
		s=$[ $1+$2  ]
		echo $s
	fi
	
	[root@ybt ~]# ./ybt.sh 3 	执行脚本

	参数个数不对

	[root@ybt ~]# ./ybt.sh 3 4	执行脚本
	7


$*和$@变量用来抓取命令行所有的参数。
	$*变量会将命令行上提供的所有参数当作一个单词保存。
	$@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。
	#这样看没有问题
	[root@ybt ~]# cat canshu1.sh 
	#!/bin/bash
	echo "$*"
	echo "$@"
	
	[root@ybt ~]# ./canshu1.sh ybt  ke
	ybt ke
	ybt ke


	#循环后问题出现
	[root@ybt ~]# cat canshu1.sh 
	#!/bin/bash
	for i in "$*"		#当然变量不加双引号,两个是一样的
	do 
		echo '$*: '$i
	done
	
	for s in "$@"
	do 
		echo '$@:' $s
	done
	
	[root@ybt ~]# ./canshu1.sh ybt  ke  执行脚本
	$*: ybt ke
	$@: ybt
	$@: ke
	
	

移动变量

shift默认它会将每个参数变量向左移动一个位置,$2会移动到$1的位置,$1则被移出;$0程序名不变
#某个参数被移出,它的值就被丢弃了,无法再恢复。
#shift  2 可以一次左移两个位置
#-- 双破折线来表明选项列表结束。双破折线就是命令行参数

脚本示例
	#!/bin/bash
	while [ -n "$1" ]
	do
	case "$1" in
	-a)
		echo 有-a选项;;
	-b)	
		echo 有-b选项;;
	-c)
		echo 有-c选项;;
	--)
		shift		#移出了 命令行参数
		break;;
	*)
		echo 没有这些选项;;
	esac
	shift 			# 移除了--双破折线
	done
	
	for i in "$@"
	do
		echo $i
	done
	
	[root@ybt ~]# ./shift.sh -b  -c --   ybt  执行脚本
	有-b选项
	有-c选项
	ybt
	
		

gotopt

14.4.2

getopt命令可以接受一系列任意形式的命令行选项和参数
命令格式
	getopt optstring parameters
#optstring它定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值。
#在optstring中列出你要在用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后加一个冒号
	#例getopt ab:cd -a -b test1 -cd test2 test3
	-a -b test1 -c -d -- test2 test3
#指定不在optstring中的选项,会报错
#加上-q后getopt -q 不在 报错
	#例getopt -q ab:cd -a -b test1 -cde test2 test3

set命令的选项之一是双破折线(--),它会将命令行参数替换成set命令的命令行值。
脚本示例
	输出 'll'
	[root@ybt ~]# cat getopt.sh 
	#!/bin/bash
	set -- $(getopt -q ab:cd "$@")
	while [ -n "$1" ]
	do
	case  "$1" in
	-a)
		echo "本脚本有 -a";;
	-b)
		ke="$2"
		echo "本脚本有  $ke "
		shift;;		#移除$1 -b选项$2变量的值
	-c)
		echo "本脚本有 -c ";;
	-d)
	        echo "本脚本有 -d";;
	--)
		shift		#这个shift在循环中移走了参数
		break;;
	*)
		echo "没有这些参数";;
	esac
	shift	#移走了--
	done
	
	for i in "$@"
	do
		echo 输出 $i
	done
	[root@ybt ~]# ./getopt.sh -ab ybt -cd ke   ll  脚本执行
	本脚本有 -a
	本脚本有  'ybt' 
	本脚本有 -c 
	本脚本有 -d
	输出 'ke'		#因为while部分遍历不到,就被for遍历输出了
	输出 'll'
	

gotopts

命令格式
	getopts optstring variable
#有效的选项字母都会列在optstring中,如果选项字母要求有个参数值,就加一个冒号。要去掉错误消息的话,可以在optstring之前加一个冒号。getopts命令将当前参数保存在命令行中定义的variable中
#如果选项需要跟一个参数值,OPTARG环境变量就会保存这个值,也保存了参数列表中getopts正在处理的参数位置
#不用定义单破折线,while语句定义了getopts命令


脚本示例
	[root@ybt ~]# cat getopts.sh 
	#!/bin/bash
	while getopts :a:c ybt	#while语句定义了getopts命令:避免报错ab选项:有参数c选项,ybt每次迭代储存它们的变量名
	do
		case "$ybt" in
	a)
		echo 此脚本有-a 值为"$OPTARG";;
	c)
		echo 此脚本有 -c;;
	*)
		echo 没有此选项 "$ybt";;	
		esac
	done
	
	[root@ybt ~]# ./getopts.sh -a ke -c  执行脚本 
	此脚本有-a 值为ke
	此脚本有 -c					#结果都移出了单破折线
	[root@ybt ~]# ./getopts.sh -a "ybt kexiao" -c ybt #引号中间可以写空格
	此脚本有-a 值为ybt kexiao
	此脚本有 -c
	[root@ybt ~]# ./getopts.sh "-aybt kexiao" -c ybt #选项与参数放一起
	此脚本有-a 值为ybt kexiao
	此脚本有 -c
	[root@ybt ~]# ./getopts.sh -a ke -c -de		#将没有的选项输出为?
	此脚本有-a 值为ke
	此脚本有 -c
	没有此选项 ?
	没有此选项 ?

在getopts处理每个选项时,它会将OPTIND环境变量值增一
脚本示例
	[root@ybt ~]# cat getopts.sh 
	#!/bin/bash
	while getopts :a:c ybt
	do
		case "$ybt" in
	a)
		echo 此脚本有-a 值为"$OPTARG";;
	c)
		echo 此脚本有 -c;;
	*)
		echo 没有此选项 "$ybt";;
		esac
	done
	
	echo $OPTIND
	shift $[ $OPTIND -1  ]			#减去默认位$0是1,增加几位移动几位
	
	for i in "$@"
	do
		echo $i
	done
	
	[root@ybt ~]# ./getopts.sh -a ybt -c kk ll	脚本执行
	此脚本有-a 值为ybt
	此脚本有 -c
	4
	kk
	ll
	
	

用户输入

read

read命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后,read命令会将数据放进一个变量
#read -p 直接在read命令行输入
脚本示例
	[root@ybt ~]# cat read.sh 
	#!/bin/bash
	echo -n "请输入你的name:"
	read k				#将用户输入信息报错到变量 k
	echo "欢迎,$k"
	
	read -p "请输入你的年龄:" nl	#直接在read命令行输入,将用户输入信息报错到变量nl
	echo "你的年龄是 $nl"
	
	[root@ybt ~]# ./read.sh 	执行脚本
	请输入你的name:ybt
	欢迎,ybt
	请输入你的年龄:27
	你的年龄是 27
	
read命令会将提示符后输入的所有数据分配给单个变量,要么你就指定多个变量。输入的每个数据值都会分配给变量列表中的下一个变量。如果变量数量不够,剩下的数据就全部分配给最后一个变量
脚本示例
	[root@ybt ~]# cat read2.sh 
	#!/bin/bash
	read -p "请输入你的姓名;年龄;家庭:" ybt ke he
	echo "$ybt $ke $he"
	echo "$he"
脚本执行
	[root@ybt ~]# ./read2.sh 
	请输入你的姓名;年龄;家庭:ybt 27 .地球
	ybt 27 .地球
	.地球
	[root@ybt ~]# ./read2.sh 
	请输入你的姓名;年龄;家庭:ybt 27 .地球 ???	#溢出的赋值都到了最后一个变量
	ybt 27 .地球 ???
	.地球 ???

read也可以不指定变量,会默认保存在REPLY变量中
脚本示例
	[root@ybt ~]# cat read.sh 
	#!/bin/bash
	read -p "请输入你的年龄:"	 #保存到了默认变量中
	echo "你的年龄是 $REPLY"		
	
	[root@ybt ~]# ./read.sh		脚本执行
	请输入你的年龄:100
	你的年龄是 100

超时

read -t选项指定了read命令等待输入的秒数。当计时器过期后,read命令会返回一个非零退出状态码
脚本示例
	[root@ybt ~]# cat read3.sh 
	#!/bin/bash
	if read -t 3 -p "你的名字是:" ybt ;then
		echo "欢迎,$ybt"
	else
		echo
		echo "您输入的太慢了,系统已退出";fi
脚本执行
	[root@ybt ~]# ./read3.sh 
	你的名字是:ybt
	欢迎,ybt
	[root@ybt ~]# ./read3.sh 
	你的名字是:					#3秒没有输入
	您输入的太慢了,系统已退出

接受指定-n字符输入个数
脚本示例
	[root@ybt ~]# cat read4.sh 
	#!/bin/bash
	if read -n1 -p "是否继续:[Y/N]" ybt;then
	case "$ybt" in
	Y|y)
		echo
		echo "请继续输入命令";;
	N|n)
		echo
		echo "再见"
		exit;;
	*)
		echo
		echo "请输入:Y/N";;
	esac
	fi

脚本执行
	[root@ybt ~]# ./read4.sh 		
	是否继续:[Y/N]y			#由于是-n1 所以一次只能输入一个字符
	请继续输入命令
	[root@ybt ~]# ./read4.sh 
	是否继续:[Y/N]n
	再见
	[root@ybt ~]# ./read4.sh 
	是否继续:[Y/N]l
	请输入:Y/N

-s选项可以避免在read命令中输入的数据出现在显示器上
	#(实际上,数据会被显示,只是read命令会将文本颜色设成跟背景色一样)
脚本示例
	[root@ybt ~]# cat read5.sh 
	#!/bin/bash
	read -s -p "请输入密码:" s
	echo 
	echo "你的密码是:$s"
	
	[root@ybt ~]# ./read5.sh 脚本执行  
	请输入密码:
	你的密码是:297878641

读取文件内容
	[root@ybt ~]# cat ybt.txt #查看文件内容
	hello world
	ni kan dao le ma 
脚本示例
	[root@ybt ~]# cat read6.sh 
	#!/bin/bash
	cat /root/ybt.txt | while read ybt
	do
		echo $ybt
	done
	
	[root@ybt ~]# ./read6.sh 脚本执行
	hello world
	ni kan dao le ma

输入输出

Linux系统将每个对象当作文件处理。这包括输入和输出进程。Linux用文件描述符来标识每个文件对象。文件描述符是一个非负整数,可以唯一标识会话中打开的文件。每个进程一次最多可以有九个文件描述符。shell保留了前三个文件描述符(0、1和2)
	0 STDIN 标准输入
	1 STDOUT 标准输出
	2 STDERR 标准错误

0 STDIN 标准输入
	[root@ybt ~]# cat ybt.txt	查看文件内容
	hello world
	ni kan dao le ma 
	
	#在cat命令会用文件中的行作为输入
	[root@ybt ~]# cat < ybt.txt		
	hello world
	ni kan dao le ma
	#可以使用这种技术将数据输入到任何能从STDIN接受数据的shell命令中!!!

1 STDOUT 标准输出
	> 重定向 
	>> 追加
	
2 STDERR 标准错误
	2> 错误重定向
	2>> 错误追加
	&> 正确错误重定向
	&>> 正确错误追加

临时重定向

脚本生成错误消息,需要重定向到STDERR文件描述符。在重定向到文件描述符时,你必须在文件描述符数字之前加一个&
脚本示例
	[root@ybt ~]# cat stderr.sh 
	#!/bin/bash
	echo "警告!!!这是一条错误" >&2	#错误消息生成格式
	echo "正常输出...."
脚本执行
	[root@ybt ~]# ./stderr.sh 	#输出时都是正常输出
	警告!!!这是一条错误
	正常输出....
	[root@ybt ~]# ./stderr.sh 2>error.txt		#将错误信息重定向到error.txt
	正常输出....
	[root@ybt ~]# cat error.txt  #可以单独查看错误信息
	警告!!!这是一条错误

永久重定向

exec命令告诉shell在脚本执行期间重定向某个特定文件描述符。
	#当你只想将脚本的部分输出重定向到其他位置时,比较实用
脚本示例
	[root@ybt ~]# cat stdout2.sh 
	#!/bin/bash
	exec 2>error.txt	#正常消息直接输出屏幕,错误消息将被2>重定向
	echo "输出。。"
	echo "输出2。。"
	echo "输出5。。" >&2

	exec 1>test			#正常消息直接1>重定向,错误消息将被2>重定向
	echo "输出3。。"
	echo "输出4。。" >&2 
脚本执行
	[root@ybt ~]# ./stdout2.sh 	#将非错误输出的输出到屏幕
	输出。。
	输出2。。
	[root@ybt ~]# cat error.txt 	#将错误输出依然输出到2>指定的文件
	输出5。。
	输出4。。
	[root@ybt ~]# cat test 	#1>正常重定向,执行正常
	输出3。。
	
脚本中重定向输入
	#exec命令允许你将STDIN重定向到Linux系统上的文件中
	exec 0< file 	#这个命令会告诉shell它应该从文件file中获得输入
脚本示例
	[root@ybt ~]# cat stdin.sh 
	#!/bin/bash
	exec 0< ybt.txt
	while read ybt;do
		echo $ybt
	done 
	
	[root@ybt ~]# ./stdin.sh 
	hello world
	ni kan dao le ma

创建输出文件描述符

用exec命令来给输出分配文件描述符
	#除了默认的012从3~8的文件描述符均可用作输入或输出重定向。
	#和标准的文件描述符一样,一旦将另一个文件描述符分配给一个文件,这个重定向就会一直有效,直到你重新分配
脚本示例
	[root@ybt ~]# cat stdout3.sh 
	#!/bin/bash
	exec 3> test3.txt		#创建文件描述符
	echo "分配文件描述符示例"
	echo "这个将指定重定向" >&3

	[root@ybt ~]# ./stdout3.sh   脚本执行
	分配文件描述符示例
	[root@ybt ~]# cat test3.txt   查看专属重定向
	这个将指定重定向

重定向文件描述符

脚本中临时重定向输出,然后恢复默认输出设置的常用方法
脚本示例
	[root@ybt ~]# cat stdout4.sh 
	#!/bin/bash
	exec 3>&1
	exec 1> text4.txt
	echo "将自定义文件描述符指向默认输出文件描述符"
	echo "默认重定向到文件了"
	exec 1>&3
	echo "将输出到屏幕,因为上面的3指向默认1"

	[root@ybt ~]# ./stdout4.sh 脚本执行
	将输出到屏幕,因为上面的3指向默认1
	[root@ybt ~]# cat text4.txt 	文件查看
	将自定义文件描述符指向默认输出文件描述符
	默认重定向到文件了

创建输入文件描述符

与输出一样
脚本示例
	[root@ybt ~]# cat stdin2.sh 
	#!/bin/bash
	exec 9<&0		#将默认输入指向9
	exec 0< ybt.txt		#将文件重定向输入到0,现在0指向9,所以是输入到了自定义9
	while read ybt;do
		echo $ybt
	done
	
	exec 0<&9		#将文件描述符复位
	
	[root@ybt ~]# ./stdin2.sh 	执行脚本
	hello world
	ni kan dao le ma

lsof

lsof命令会列出整个Linux系统打开的所有文件描述符
-i 选择ipv4或6文件
	[root@ybt ~]# lsof -i:3306   #可以查看端口对应服务
	COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
	mysqld  1227 mysql   29u  IPv6  20695      0t0  TCP *:mysql (LISTEN)


列出打开的文件描述符 
变量$$(shell会将它设为当前PID)。
#-p指定进程PID ; -d许指定要显示的文件描述符编号; -a选项用来对其他两个选项的结果执行布尔AND运算,
脚本示例
	[root@ybt ~]# cat lsof.sh 
	#!/bin/bash
	exec 3>test.lsof
	exec 4<test1.lsof
	lsof -a -p $$ -d 0,1,2,3,4
	
	[root@ybt ~]# ./lsof.sh 	执行脚本
	COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF      NODE NAME
	lsof.sh 82786 root    0u   CHR  136,0      0t0         3 /dev/pts/0
	lsof.sh 82786 root    1u   CHR  136,0      0t0         3 /dev/pts/0
	lsof.sh 82786 root    2u   CHR  136,0      0t0         3 /dev/pts/0
	lsof.sh 82786 root    3w   REG  253,0        0 140392881 /root/test.lsof	
	lsof.sh 82786 root    4r   REG  253,0        0 140392883 /root/test1.lsof
	
	#FD 文件描述符号以及访问类型(r代表读,w代表写,u代表读写)
	#TYPE 文件的类型(CHR代表字符型,BLK代表块型,DIR代表目录,REG代表常规文件)
	#DEVICE 设备的设备号(主设备号和从设备号)
	#NODE 本地文件的节点号

null文件

​ /dev/null 你重定向到该位置的任何数据都会被丢掉,不会显示

临时文件

/tmp目录来存放不需要永久保留的文件
	大多数Linux发行版配置了系统在启动时自动删除/tmp目录的所有文件。#CentOs好像并没有删除

mktemp 创建临时文件
示例
	[root@ybt ~]# mktemp ybt.XXX		#最低三位XXX
	ybt.nav			
	[root@ybt ~]# mktemp ybt.XXXXXXX
	ybt.yFZzt5A
	-rw-------. 1 root root   0 3月  23 21:12 ybt.nav #它们的权限都是rw属主权限
	[root@ybt ~]# mktemp -t ybt.XXX		-t强制在/tmp临时目录创建文件
	/tmp/ybt.ZSb
	[root@ybt ~]# mktemp -d mul.XXX		-d创建临时目录 
	mul.Myx
	[root@ybt ~]# cd mul.Myx/
	[root@ybt mul.Myx]# ll
	总用量 0



脚本示例
	[root@ybt ~]# cat mktemp.sh 	
	#!/bin/bash
	ybt=$(mktemp -t ybt.XXX)	#在/tmp下创建文件
	echo "这是对临时文件重定向值" > $ybt
	echo "追加" >> $ybt
	cat $ybt	#查看文件信息
	ls -l $ybt
	rm $ybt 
	
	[root@ybt ~]# ./mktemp.sh 	执行脚本
	这是对临时文件重定向值
	追加
	-rw-------. 1 root root 41 3月  23 21:23 /tmp/ybt.i4F
	
tee将输入过来的数据一个发送到屏幕,一个发送到文件,默认重定向,-a是追加
脚本示例
	[root@ybt ~]# cat tee.sh 
	#!/bin/bash
	ybt=$(mktemp -t ybt.XXX)
	cat ybt.txt|tee $ybt
	cat $ybt
	
	[root@ybt ~]# ./tee.sh 	脚本执行
	hello world			#tee输出和追加
	ni kan dao le ma 
	hello world
	ni kan dao le ma 
	
文件追加多行内容
    [root@ybt ~]# cat file
    [root@ybt ~]# cat >> file <<EOP
    > 1
    > 2
    > 3
    > EOP
    [root@ybt ~]# cat file
    1
    2
    3
    
书本15.9实例,可以给出多个重定向使用,脚本灵感

控制脚本

linux信号

#bash shell会忽略收到的任何SIGQUIT (3)和SIGTERM (15)信号
#不会忽略1 SIGHUP 挂起进程和2 SIGINT 终止进程
	1 SIGHUP 挂起进程
	2 SIGINT 终止进程
	3 SIGQUIT 停止进程
	9 SIGKILL 无条件终止进程
	15 SIGTERM 尽可能终止进程
	17 SIGSTOP 无条件停止进程,但不是终止进程
	18 SIGTSTP 停止或暂停进程,但不终止进程
	19 SIGCONT 继续运行停止的进程

生成信号

Ctrl+c生成 SIGINT 终止进程,并将其发送给当前在shell中运行的所有进程 
Ctrl+Z生成一个SIGTSTP信号,停止shell中运行的任何进程。 # 挂起
kill  SIGKILL 无条件终止进程

捕捉信号

trap #捕获信号,忽略信号,交给本地shell处理
命令格式
	trap commands signals 
脚本示例
	[root@ybt ~]# cat trap.sh 
	#!/bin/bash
	trap "echo 'CTRL+C 终止失败!!'" SIGINT		#忽略CTRL+C终止信号
	y=1
	while [ $y -le 10 ];do
		echo $y
		sleep 1
		y=$[ $y+1 ]
	done

	[root@ybt ~]# ./trap.sh	#脚本执行
	1
	2
	3
	^CCTRL+C 终止失败!!	#CTRL+C没用了
	4
	5
	6
	^CCTRL+C 终止失败!!
	7
	8
	^CCTRL+C 终止失败!!
	9
	10

捕获脚本退出信号
脚本示例
	[root@ybt ~]# cat trap2.sh 
	#!/bin/bash
	trap "echo 再见了,我的小脚本" exit
	
	y=1
	while [ $y -le 3 ];do
		echo $y
		y=$[ $y+1 ];done
		
	[root@ybt ~]# ./trap2.sh 	脚本执行
	1
	2
	3
	再见了,我的小脚本
	

修改和删除捕捉信号
脚本示例 --删除
	[root@ybt ~]# cat trap3.sh 
	#!/bin/bash
	trap "echo 你不能使用CTRL+C" SIGINT
	ybt=1
	while [ $ybt -le 4 ];do
		echo $ybt
		sleep 1
		ybt=$[ $ybt+1 ];done
	trap -- SIGINT					#删除捕捉信号
	ybt=1
	while [ $ybt -le 4 ];do
	        echo $ybt
	        sleep 1
	        ybt=$[ $ybt+1 ];done
	        
	[root@ybt ~]# ./trap3.sh 执行脚本
	1
	2
	3
	^C你不能使用CTRL+C
	4
	1
	2
	^C
	
脚本示例 --修改
	[root@ybt ~]# !cat 
	cat trap3.sh  
	#!/bin/bash
	trap "echo 你不能使用CTRL+C" SIGINT
	ybt=1
	while [ $ybt -le 4 ];do
		echo $ybt
		sleep 1
		ybt=$[ $ybt+1 ];done
	
	trap "echo !!在次警告,你不能使用CTRL+C" SIGINT		#修改捕捉信号
	ybt=1
	while [ $ybt -le 4 ];do
	        echo $ybt
	        sleep 1
	        ybt=$[ $ybt+1 ];done
	
	[root@ybt ~]# ./trap3.sh 	脚本执行
	1
	2
	3
	^C你不能使用CTRL+C
	4
	1
	2
	^C!!在次警告,你不能使用CTRL+C
	3
	4
	
	

后台运行

脚本就算后台运行也会有输出,可以将结果重定向
	[root@ybt ~]# ./trap3.sh &
	[1] 3001
	[root@ybt ~]# 1
	2
	3
	4
	1
	2
	3
	4
	
	[1]+  完成                  ./trap3.sh

nohup命令让脚本一直以后台模式运行到结束,即使退出终端
	#nohup命令运行了另外一个命令来阻断所有发送给该进程的SIGHUP信号,这会在退出终端会话时阻止进程退出
	[root@ybt ~]# nohup ./trap3.sh &		加&执行脚本
	[1] 3011
	[root@ybt ~]# nohup: 忽略输入并把输出追加到"nohup.out"
	
	[1]+  完成                  nohup ./trap3.sh
	
	不加&,#所以不用加&,因为本身就是将命令放在后台执行
	[root@ybt ~]# nohup ./trap3.sh 
	nohup: 忽略输入并把输出追加到"nohup.out"
	[root@ybt ~]# cat nohup.out 	#查看该命令的默认追加文件
	1
	2
	3
	4
	1
	2
	3
	4

函数

#函数代码块会在调用的函数地方执行完后,继续执行调用函数代码后方的代码!
#函数一个原则就是只干一件事
语法格式
		function name { 
		 commands 
		}
第二种
		name() { 
		commands 
		}

脚本示例
	[root@ybt ~]# cat hanshu.sh 
	#!/bin/bash
	#函数测试
	ybt(){
	echo 这是一个函数
	}
	
	ybt
	
	[root@ybt ~]# ./hanshu.sh 	脚本执行
	这是一个函数

return

用return命令来退出函数并返回特定的退出状态码。return命令允许指定一个整数值来定义函数的退出状态码
脚本示例
	[root@ybt ~]# cat hanshu2.sh 
	#!/bin/bash
	ke(){
	read -p "请输入一个数值:" ybt
	return $[ $ybt*10  ]
	}
	ke			#如果在用$?变量提取函数返回值之前执行了其他命令,函数的返回值就会丢失
	echo "函数退出码是:$?"
	
	[root@ybt ~]# ./hanshu2.sh 	脚本执行
	请输入一个数值:100
	函数退出码是:232  

使用函数输出

将函数保存在变量里
脚本示例
	[root@ybt ~]# cat hanshu3.sh 
	#!/bin/bash
	ybt(){
	read -p"请输入一个数字:" y
		echo $[ $y*2 ]
	}
	
	ke=$(ybt)				#将函数赋值给变量ke
	echo "值为:" $ke
	
	[root@ybt ~]# chmod +x hanshu3.sh  脚本执行
	[root@ybt ~]# ./hanshu3.sh 
	请输入一个数字:100
	值为: 200			#shell只是将函数值拿走了
	



向函数传递参数

#由于函数使用特殊参数环境变量作为自己的参数值,因此它无法直接获取脚本在命令行中的参数值
格式
	函数名 $value1 10
脚本示例
	[root@ybt ~]# cat hanshu4.sh 	
	#!/bin/bash
	ybt(){
	echo $[ $1+$2  ]
	echo $[ $3+$4  ]
	echo $[ $3*$4  ]
	}

	y=$(ybt 20 30 40 50)		#传参格式也可以``
	echo $y
	
	[root@ybt ~]# ./hanshu4.sh 脚本执行
	50 90 2000


向函数传数组参数
脚本展示
	[root@ybt ~]# cat hanshu6.sh
	#!/bin/bash
	ybt(){
		echo $@ }
	
	s=(0 1 2 3 4 5)	#必须将该数组变量的值分解成单个的值
	ybt ${s[*]}
	
	[root@ybt ~]# ./hanshu6.sh	脚本执行 
	0 1 2 3 4 5

从函数返回数组
脚本示例
	[root@ybt ~]# cat hanshu6.sh 
	#!/bin/bash
	ybt(){
		a2=(`echo $@`)
		a1=$[ $#-1]
		for (( i=0 ;i<=$a1;i++));do
			a2[$i]=$[ ${a2[$i]} * 2  ];done
	echo ${a2[*]}
	}
	
	s=(20 30  40 50)
	ybt ${s[*]}
	
	[root@ybt ~]# ./hanshu6.sh 	脚本执行;返回了数组并*2
	40 60 80 100

函数变量

分为全局变量和局部变量
全局变量
	默认情况下,你在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问。
局部变量
	local关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字的变量,那么shell将会保持这两个变量的值是分离的
	local temp 
	
脚本示例
	[root@ybt ~]# cat hanshu5.sh 
	#!/bin/bash
	ybt(){
	local ke=$[ $tian + 5  ]		#设置了本地变量不与外边变量重复
	ti=$[ $ke * 2  ]
	}
	
	ke=4
	tian=6
	ybt
	echo "$ke  $ti"
	echo "输出ti:$ti"
	if [ $ke -ge $tian ];then
		echo ke 大
	else
		echo ke 小;fi
	[root@ybt ~]# ./hanshu5.sh 
	4  22				#ke无法输出函数定义的local变量值
	输出ti:22
	ke 小

函数递归

递归算法的经典例子是计算阶乘
脚本示例
	[root@ybt ~]# cat hanshu7.sh 
	#!/bin/bash
	#计算乘阶:一个数的阶乘是该数之前的所有数乘以该数的值 	如:3的乘阶 1*2*3
	ybt(){
		if  [ $1 -eq 1  ];then
			echo 1
		else
			ke=$[ $1-1  ]
			y1=`ybt $ke`		#这一行代码,请重复观看
			echo $[ $y1*$1  ];fi
	}
	
	read -p "请输入数字,我们帮你计算乘阶:" y2
	y3=`ybt $y2`
	echo "输入"$y2";乘阶计算值为:$y3 "
	
	[root@ybt ~]# ./hanshu7.sh 	执行脚本
	请输入数字,我们帮你计算乘阶:6
	输入6;乘阶计算值为:720 

库文件

创建库文件
脚本示例
	[root@ybt ~]# cat kuwenjian.sh 		
	#!/bin/bash		
	. ./hanshu7.sh	#创建库文件,相当于引用这个脚本,当前引用的是本目录下的脚本,
				#source命令有个快捷的别名,称作点操作符(dot operator)就是路径前边的点
	
	[root@ybt ~]# ./kuwenjian.sh 	脚本执行
	请输入数字,我们帮你计算乘阶:10
	输入10;乘阶计算值为:3628800 
	
脚本示例
	[root@ybt ybt]# pwd 	路径
	/home/ybt
	[root@ybt ybt]# cat kuwenjian.sh  定义的函数脚本
	#!/bin/bash
	ybt(){
		echo [ $1 + $2 ]
	}
	
	ke(){
		echo [ $1 * $2 ]
	}
	
	[root@ybt ~]# cat ybt.sh 	库文件
	#!/bin/bash
	. /home/ybt/kuwenjian.sh		#.是source命令
	ybt 10 20
	ke 10 20
	
	[root@ybt ~]# ./ybt.sh		#执行脚本
	30
	200

命令行定义函数
	#退出shell后自动消失
	#永久函数生效在.bashrc里定义,或者直接在它里边定义库文件,或者在他的/etc/bashrc里定义
	#定义完后source加载
	[root@ybt ~]# ybt (){
	> echo $[ $1+$2 ]
	> }
	
	[root@ybt ~]# ybt 10 20		执行
	30

	

编译安装shtool库

wget ftp://ftp.gnu.org/gnu/shtool/shtool-2.0.8.tar.gz
tar -xf shtool-2.0.8.tar.gz 
ll
cd shtool-2.0.8/
./confifgure		#configure命令会检查构建shtool库文件所必需的软件
make				#make命令负责构建shtool库文件
make test			#make test测试库文件
make install		#make install安装

shtool库函数
	Arx 创建归档文件(包含一些扩展功能)
	Echo 显示字符串,并提供了一些扩展构件
	fixperm 改变目录树中的文件权限
	install 安装脚本或文件
	mdate 显示文件或目录的修改时间
	mkdir 创建一个或更多目录
	Mkln 使用相对路径创建链接
	mkshadow 创建一棵阴影树
	move 带有替换功能的文件移动
	Path 处理程序路径
	platform 显示平台标识
	Prop 显示一个带有动画效果的进度条
	rotate 转置日志文件
	Scpp 共享的C预处理器
	Slo 根据库的类别,分离链接器选项
	Subst 使用sed的替换操作
	Table 以表格的形式显示由字段分隔(field-separated)的数据
	tarball 从文件和目录中创建
	version 创建版本信息文件

使用shtool库函数
	命令格式
	shtool [options] [function [options] [args]]
	示例
	[root@ybt ~]# ./trap3.sh |shtool prop -p '稍等...'
	稍等......	#显示了带动画的进度条

菜单

布局

将菜单布局作为一个函数
脚本示例
	[root@ybt ~]# cat caidan.sh 
	#!/bin/bash
	caidan (){
		local t="\t\t"
		clear			#清屏
		echo -e "$t 菜单布局示例\n"
		echo -e "$t 请选择你的选项\n"
		echo -e "$t 1.删除\n$t 2.查询\n$t 3.修改\n$t 4.新增\n"
		echo -en "$t 请输入:"
		read -n1 ybt
	}
	caidan

	[root@ybt ~]# ./caidan.sh 	脚本执行
		 菜单布局示例

		 请选择你的选项

		 1.删除
		 2.查询
		 3.修改
		 4.新增

		 请输入:

逻辑

case命令最适合
脚本示例
	[root@ybt ~]# cat caidan2.sh 
	#!/bin/bash
	. ./caidan.sh
	while [ 1 ];do
		caidan
		case $ybt in
		1)
			echo -e "\n已删除"
			read;;					#read可以写其他命令执行后break,或进入其他函数
		2)
			echo -e "\n查询结果为"
			read;;
		3)
			echo -e "\n修改结果为"
			read;;
		4)
			echo -e "\n新增结果为"
			read;;
		*)
			echo -e "\n没有这个选项,请重新选择"
			read;;
		esac
	done
	
	[root@ybt ~]# ./caidan2.sh 	脚本执行
		 菜单布局示例

		 请选择你的选项

		 1.删除
		 2.查询
		 3.修改
		 4.新增

		 请输入:9
	没有这个选项,请重新选择

select

语法格式	#挺有用
	select variable in list 		#可以使用break,continue
	do 
	 commands 
	done
		#list参数是由空格分隔的文本选项列表;选项提示保存PS3环境变量中
脚本示例
	[root@ybt ~]# cat select.sh 
	#!/bin/bash
	PS3="请输入 :"
	select ybt in "删除" "查询"	#保存在变量里的是具体值,不是编号,编号是后继生成的
	do 
		case $ybt in
		"删除")
			echo "已删除";;
		"查询") 
			echo "结果为";;
		*)
			echo "没有此选项";;
		esac
	done
	
	[root@ybt ~]# ./select.sh 	脚本执行
	1) 删除
	2) 查询
	请输入 :1
	已删除
	请输入 :2
	结果为
	请输入 :

dialog

#了解制作窗口图形shell编程
安装
	yum -y install dialog
dialog部件
	calendar 提供选择日期的日历
	checklist 显示多个选项(其中每个选项都能打开或关闭)
	form 构建一个带有标签以及文本字段(可以填写内容)的表单
	fselect 提供一个文件选择窗口来浏览选择文件
	gauge 显示完成的百分比进度条
	infobox 显示一条消息,但不用等待回应
	inputbox 提供一个输入文本用的文本表单
	inputmenu 提供一个可编辑的菜单
	menu 显示可选择的一系列选项
	msgbox 显示一条消息,并要求用户选择OK按钮
	pause 显示一个进度条来显示暂定期间的状态
	passwordbox 显示一个文本框,但会隐藏输入的文本
	passwordform 显示一个带标签和隐藏文本字段的表单
	radiolist 提供一组菜单选项,但只能选择其中一个
	tailbox 用tail命令在滚动窗口中显示文件的内容
	tailboxbg 跟tailbox一样,但是在后台模式中运行
	textbox 在滚动窗口中显示文件的内容
	timebox 提供一个选择小时、分钟和秒数的窗口
	yesno 提供一条带有Yes和No按钮的简单消息

命令格式
	dialog --widget parameters  #--部件名  定义窗口大小和需要的文本
	
msgbox	#它会在窗口中显示一条简单的消息,直到用户单击OK按钮后才消失
	dialog --msgbox "欢迎使用ybt脚本\n请尽情使用" 100 100 

title #请选择为标题,yes/no为内容 ,--yesno为选项,用户选择yes退出状态码为0,no为1
 	dialog --title "请选择" --yesno "yes/no" 100 100
	
	#"选择文件"--fselect /root/ 指定目录,选择内容,空格选定,回车确认 宽,高
	dialog --title "选择文件" --fselect /root/ 20 50 2>test.txt
	
inputbox #提供简单用户文本输入,"请输入内容:"提示用户 高度 宽度 ,必须STDERR重定向来获取用户输入,用户选择yes退出状态码为0,no为1
	dialog --inputbox "请输入内容:" 100 200 2>test.txt

textbox #是在窗口中显示大量信息的极佳办法
	dialog --textbox ./ybt.sh 100 100  #查看了同目录下的ybt.sh脚本内容

menu #提供选项"请选择" 高 款 最大选项 1 删除 2 查看 ;将编号附加到文本;方向键控制
	dialog --menu "请选择"  100 100 10 1 "删除" 2 "查看" 2>test.tx
	
	
	
dialog其他选项 书本-p391
	--backtitle选项是为脚本中的菜单创建公共标题

dialog 脚本示例

[root@ybt ~]# cat tuxing.sh 
#!/bin/sh
tp1=`mktemp -t test.XXX`
if which dialog
then
	echo
	elif
	yum -y install dialog > $tp1
then
	echo
	else
	echo "尝试yum安装dialog失败!!\n请安装 dialog库\n本脚本已退出...."
	exit	
fi

cp1(){
	df -h > $tp1
	dialog --textbox $tp1 50 100
}
cp2(){
	cat /etc/redhat-release > $tp1
	dialog --textbox $tp1 50 100
}
cp3(){
	free -h > $tp1
	dialog --textbox $tp1 50 100
}

while [ 1  ];do
if	dialog --menu "请选择" 100 100 10 1 "查看磁盘使用情况" 2 "查看系统版本信息" 3 "查看内存信息" 4 "退出" 2> $tp1
then
	case `cat $tp1` in
	1)
		cp1;;
	2)
		cp2;;
	3)
		cp3;;
	4)
		exit;;
	*)
	dialog --msgbox "没有此选项!!" 10 30;;
	esac
else
	exit
fi
done

sed

sed流编辑器
命令格式
	sed options script file
选项和参数
	p print 输出信息
	i insert 插入信息,在指定信息前面插入新的信息
	a append 附加信息,在指定信息后面附加新的信息
	d delete 删除指定信息
	c 替换指定的一整行信息
	y 转换命令
	r 读取(read)允许你将一个独立文件中的数据插入到数据流中
	l 列出(list)可以打印数据流中的文本和不可打印的ASCII字符。
		/内容/  (固定格式)
	-f	   指定文件,放入sed命令
	-n      取消默认输出
	-i  	真替换
	-r 		识别扩展正则
	-e	 	识别sed命令多个操作指令
	关于替换
	s substitute 替换信息g结尾	# s substitute 替换 g global  将整行符合要求的字符都做替换
		w 将替换的结果写到文件中 
		数字 表示新文本替换第几处模式匹配的地方
	
测试替换		#测试替换只是看替换后结果,没有真生效 -i真生效
	#查看源文件
	[root@ybt ybt]# cat ybt.txt 	
	111111111111111
	222222222222222
	333333333333333

	#测试替换将1替换成Ybt
	[root@ybt ybt]# sed "s/1/ybt/g" ybt.txt 	
	ybtybtybtybtybtybtybtybtybtybtybtybtybtybtybt
	222222222222222
	333333333333333


指定多个编辑命令
	[root@ybt ybt]# sed -e "s/1/ybt/g; s/2/ke/g " ybt.txt 
		或:sed -e 's/1/ybt/g' -e 's/2/ke/g' ybt.txt
	ybtybtybtybtybtybtybtybtybtybtybtybtybtybtybt
	kekekekekekekekekekekekekekeke
	333333333333333


从文件读取命令
	[root@ybt ybt]# cat ke 	#将命令放入ke文件
	s/1/old/g
	s/2/boy/g
	s/3/ybt/g
	
	[root@ybt ybt]# sed -f ke ybt.txt  #-f指定后测试
	oldoldoldoldoldoldoldoldoldoldoldoldoldoldold
	boyboyboyboyboyboyboyboyboyboyboyboyboyboyboy
	ybtybtybtybtybtybtybtybtybtybtybtybtybtybtybt


替换单个-n取消了默认输出;p又输出了一行,
	#如果你不加-n会看到符合的输出了两行,这是因为加了p
	#由于没有g所有只做了只替换每行中出现的第一处
	[root@ybt ybt]# sed -ne "s/1/ybt/p; s/2/ke/ p" ybt.txt 
	ybt11111111111111
	ke22222222222222

只替换第三处的匹配
	[root@ybt ybt]# sed -n 's/1/y/3p' ybt.txt 
	11y111111111111

真替换
	[root@ybt ybt]# sed  -i  's/1/y/3' ybt.txt 		#-i直接源文件替换
	[root@ybt ybt]# cat ybt.txt
	11y111111111111
	222222222222222
	333333333333333

	[root@ybt ybt]# sed 's/1/ss/3w bf.txt' ybt.txt  #w写
	11ss111111111111
	222222222222222
	333333333333333
	[root@ybt ybt]# cat bf.txt 	#将替换后的内容写到了新文件bf.txt
	11ss111111111111
	[root@ybt ybt]# cat ybt.txt		#源文件不变
	111111111111111
	222222222222222
	333333333333333

目录替换可以用#或!
	[root@ybt ybt]# cat gantan.txt  #查看文件信息
	/ybt/ke/
	[root@ybt ybt]# sed 's/\/ybt\/ke\//\/ke\/ybt\//g' gantan.txt  就是用了这么多转义字符,容易出错
	/ke/ybt/
	[root@ybt ybt]# sed 's!/ybt/ke/!/ke/ybt/!g' gantan.txt 	使用!
	/ke/ybt/
	[root@ybt ybt]# sed 's#/ybt/ke/#/ke/ybt/#g' gantan.txt 	使用#
	/ke/ybt/

删除d

删除d
	测试
	[root@ybt ybt]# cat yb
	chou gou zi ni shi shei na ge gou	1
	chou gou zi ni shi shei na ge gou	2
	chou gou zi ni shi shei na ge gou	3
	chou gou zi ni shi shei na ge gou	4
	#删除整个文件内容测试
	[root@ybt ybt]# sed 'd' yb		
	
	删除指定行,第3行
	[root@ybt ybt]# sed '3d' yb
	chou gou zi ni shi shei na ge gou	1
	chou gou zi ni shi shei na ge gou	2
	chou gou zi ni shi shei na ge gou	4
	
	删除指定行,第2行4行			#其实是多条命令
	[root@ybt ybt]# sed '2d;$d' yb
	chou gou zi ni shi shei na ge gou	1
	chou gou zi ni shi shei na ge gou	3
	
	区间删除1到3行
	[root@ybt ybt]# sed '1,3d' yb
	chou gou zi ni shi shei na ge gou	4
	
	区间删除2到最后一行
	[root@ybt ybt]# sed '2,$d' yb
	chou gou zi ni shi shei na ge gou	1

文本模式匹配删除
	测试
	[root@ybt ybt]# cat yb		查看源文件
	chou gou zi ni shi shei na ge gou	1
	chou gou zi ni shi shei na ge gou	2
	chou gou zi ni shi shei na ge gou	3
	chou gou zi ni shi shei na ge gou	4
	chou gou zi ni shi shei na ge gou	1
	wo men ke neng ye hui bei shan chu

	#删除匹配到有1的行
	[root@ybt ybt]# sed '/1/d' yb
	chou gou zi ni shi shei na ge gou	2
	chou gou zi ni shi shei na ge gou	3
	chou gou zi ni shi shei na ge gou	4
	wo men ke neng ye hui bei shan chu
	
	#指定删除匹配到1匹配到3的行		
	[root@ybt ybt]# sed '/1/d;/3/d' yb
	chou gou zi ni shi shei na ge gou	2
	chou gou zi ni shi shei na ge gou	4
	wo men ke neng ye hui bei shan chu

	#区间删除匹配1打开一个区间到匹配3进行删除,之间的行都会被删除     #慎用
	#第二个出现数字“1”的行再次触发了删除命令,因为没有找到停止模式,所以剩下的内容都被删除了
	[root@ybt ybt]# sed '/1/,/3/d' yb
	chou gou zi ni shi shei na ge gou	4
	#由于没有匹配到6所以sed清理了匹配1之后所有的行		#离谱
	[root@ybt ybt]# sed '/1/,/6/d' yb

	

插入i附加a

[root@ybt ybt]# cat yt		查看源文件
chou gou zi ni shi shei na ge gou
hello world bu zhi dao xin sha
yang bi tian and zhan ke xiao

测试
	#在第2行上插入ybt				
	[root@ybt ybt]# sed '2i\ybt' yt		#转义字符可以省略,后面都相同
	chou gou zi ni shi shei na ge gou
	ybt
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao

在第2行后附加ybt
	[root@ybt ybt]# sed '2a\ybt' yt
	chou gou zi ni shi shei na ge gou
	hello world bu zhi dao xin sha
	ybt
	yang bi tian and zhan ke xiao

在第二行上插入ybt在2行后附加ke
	[root@ybt ybt]# sed  -e '2i\ybt' -e '2a\ke' yt
	chou gou zi ni shi shei na ge gou
	ybt
	hello world bu zhi dao xin sha
	ke
	yang bi tian and zhan ke xiao

在第2行上方插入helloworld和ybt
	[root@ybt ybt]# sed '2ihelloword\nybt' yt
	chou gou zi ni shi shei na ge gou
	helloword
	ybt
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao

修改c

[root@ybt ybt]# cat yt		查看源文件
chou gou zi ni shi shei na ge gou
hello world bu zhi dao xin sha
yang bi tian and zhan ke xiao

修改有gou的行为ybt
	[root@ybt ybt]# sed '/gou/c\ybt' yt			#转义符\可以省略
	ybt
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	
修改第二行为ybt 
	[root@ybt ybt]# sed '2c\ybt' yt
	chou gou zi ni shi shei na ge gou
	ybt
	yang bi tian and zhan ke xiao
	
修改区间行 
	[root@ybt ybt]# sed '1,3c\ybt' yt		#但是并未实现
	ybt
	#sed编辑器会用这一行文本来替换数据流中的3行文本,而不是逐一修改这两行文本

转换y

转换(transform)命令(y)是唯一可以处理单个字符的sed编辑器命令。
	[address]y/inchars/outchars/
	#转换命令会对inchars和outchars值进行一对一的映射。如果长度不同,会报错
	#转换命令是一个全局命令
	[root@ybt ybt]# cat yb		源文件
	chou gou zi ni shi shei na ge gou	1
	chou gou zi ni shi shei na ge gou	2
	chou gou zi ni shi shei na ge gou	3
	chou gou zi ni shi shei na ge gou	4
	chou gou zi ni shi shei na ge gou	1
	wo men ke neng ye hui bei shan chu
	#123/569长度不同,sed编辑器会报错,如123/53
	[root@ybt ybt]# sed 'y/123/569/' yb	
	chou gou zi ni shi shei na ge gou	5
	chou gou zi ni shi shei na ge gou	6
	chou gou zi ni shi shei na ge gou	9
	chou gou zi ni shi shei na ge gou	4
	chou gou zi ni shi shei na ge gou	5
	wo men ke neng ye hui bei shan chu

r、p、l、=

r 读取(read)允许你将一个独立文件中的数据附加到数据流中。
	#命令格式 [address]r filename
	
	[root@ybt ybt]# cat yt	 源文件
	chou gou zi ni shi shei na ge gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	[root@ybt ybt]# cat a1 	源文件
	1 2 3
	4 5 6
	#将a1文件内容附加到yt文件的第2行
	[root@ybt ybt]# sed '2r a1' yt
	chou gou zi ni shi shei na ge gou
	hello world bu zhi dao xin sha
	1 2 3
	4 5 6
	yang bi tian and zhan ke xiao
	#将a1文件内容附加到yt文件有gou的行
	[root@ybt ybt]# sed '/gou/r a1' yt
	chou gou zi ni shi shei na ge gou
	1 2 3
	4 5 6
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao

	#将a1文件内容附加到有sha的行并删除了有sha的一行
	[root@ybt ybt]# sed '/sha/{
	r a1
	d
	}' yt
	chou gou zi ni shi shei na ge gou
	1 2 3
	4 5 6
	yang bi tian and zhan ke xiao
		#	也可以一条命令执行
		[root@ybt ybt]# sed -e '/sha/r a1' -e '/sha/d' yt
		chou gou zi ni shi shei na ge gou
		1 2 3
		4 5 6
		yang bi tian and zhan ke xiao
	
	#不指定文本或行号默认每行都添加
	[root@ybt ~]# sed 'r a1' yt
	chou gou zi ni shi shei na ge gou
	1 2 3
	4 5 6
	hello world bu zhi dao xin sha
	1 2 3
	4 5 6
	yang bi tian and zhan ke xiao
	1 2 3
	4 5 6


p命令用来打印文本行;
等号(=)命令用来打印行号;
l 列出(list)可以打印数据流中的文本和不可打印的ASCII字符

	[root@ybt ybt]# cat yt	源文件
	chou gou zi ni shi shei na ge gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	#将第二行打印出来
	[root@ybt ybt]# sed '2p' yt		
	chou gou zi ni shi shei na ge gou
	hello world bu zhi dao xin sha
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	#-n取消默认输出,只输出打印的第二行
	[root@ybt ybt]# sed -n '2p' yt
	hello world bu zhi dao xin sha

	#打印行号
	[root@ybt ybt]# sed "=" yt
	1
	chou gou zi ni shi shei na ge gou
	2
	hello world bu zhi dao xin sha
	3
	yang bi tian and zhan ke xiao

l 列出(list)可以打印数据流中的文本和不可打印的ASCII字符
	[root@ybt ybt]# sed -n "l" yt			#-n 取消默认输出
	chou gou zi ni shi shei na ge gou$		#行尾的美元符表示换行符
	hello world bu zhi dao xin sha$
	yang bi tian and zhan ke xiao$


sed寻址

#以数字形式表示行区间
#用文本模式来过滤出行
	文本命令格式/pattern/command 

以数字形式表示行区间
	[root@ybt ybt]# cat yb	查看源文件
	chou gou zi ni shi shei na ge gou
	chou gou zi ni shi shei na ge gou
	chou gou zi ni shi shei na ge gou
	chou gou zi ni shi shei na ge gou
	#指定第二行的第二处匹配做替换测试
	[root@ybt ybt]# sed '2s/gou/B/2' yb
	chou gou zi ni shi shei na ge gou
	chou gou zi ni shi shei na ge B
	chou gou zi ni shi shei na ge gou
	chou gou zi ni shi shei na ge gou
	#指定第二行到最后一行的第二处匹配做替换测试
	[root@ybt ybt]# sed '2,$s/gou/B/2' yb			#$是最后一行
	chou gou zi ni shi shei na ge gou
	chou gou zi ni shi shei na ge B
	chou gou zi ni shi shei na ge B
	chou gou zi ni shi shei na ge B

用文本模式来过滤出行
	[root@ybt ybt]# cat yt		查看源文件
	chou gou zi ni shi shei na ge gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	#匹配gou的一行后做na向de的整行替换测试
	[root@ybt ybt]# sed '/gou/s/na/de/g' yt		
	chou gou zi ni shi shei de ge gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao

	

进阶sed

​ 建议多看几遍,书本第21章!!!

N:将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
D:删除多行组中的一行。
P:打印多行组中的一行。

next
	[root@ybt ybt]# cat a1
	212-555.1234
	ybt
	#匹配有2的行,删除它的下一行
	[root@ybt ybt]# sed  '/2/{n;d}' a1
	212-555.1234

N #多行版本的next;会将下一文本行添加到模式空间中已有的文本后。
	[root@ybt ybt]# cat ybt1.txt 源文件
	One line of test text 
	Two lines of test text 
	
	Three text Two text
	#过滤空行匹配下一行是否有Two有则d删除
	[root@ybt ybt]# sed '/^$/{N;/Two/d}' ybt1.txt 	#结果为删除了空行和空行下有Two的行
	One line of test text 
	Two lines of test text		
D 多行删除命令D,它只删除模式空间中的第一行。该命令会删除到换行符(含换行符)为止的所有字符。
	#过滤空行匹配下一行是否有Two有则D删除它的上一行
	[root@ybt ybt]# sed '/^$/{N;/Two/D}' ybt1.txt 
	One line of test text 
	Two lines of test text 
	Three text Two text
	
	#N合并
	[root@ybt ybt]# sed '=' ybt.txt | sed 'N; s/\n/ /'	#替换删除了回车
	1 111111111111111
	2 222222222222222
	3 333333333333333
	
	示例2
	[root@ybt ybt]# cat ybt1.txt	源文件
	One line of test text
	Two lines of test text
	Three text Two text
	four line test  text
	[root@ybt ybt]# sed 'N ; s/text\nTwo/ybt/' ybt1.txt
	One line of test ybt lines of test text
	Three text Two text
	four line test  text
	#没错.符号也可以代表回车
	[root@ybt ybt]# sed 'N ; s/text.Two/ybt/' ybt1.txt
	One line of test ybt lines of test text
	Three ybt text
	four line test  text


	
	
	
保持空间
	h 将模式空间复制到保持空间
	H 将模式空间附加到保持空间
	g 将保持空间复制到模式空间
	G 将保持空间附加到模式空间
	x 交换模式空间和保持空间的内容
	[root@ybt ybt]# cat ybt1.txt 	源文件
	One line of test text 
	Two lines of test text 
	Three text Two text
	#实现第一行和第二行换位
	[root@ybt ybt]# sed -n '/One/{h;n;p;g;p}' ybt1.txt 
	Two lines of test text 
	One line of test text 
	#添加空行,  因为当启动sed编辑器时保持空间为一个空行,所以就在匹配One行下面添加了空行
	[root@ybt ybt]# sed  '/One/G' ybt1.txt
	One line of test text 
	
	Two lines of test text 
	Three text Two text

! 排除
	[root@ybt ybt]# cat ybt1.txt 
	One line of test text 
	Two lines of test text 
	Three text Two text
	#匹配排除有One的所有行
	[root@ybt ybt]# sed -n '/One/!p' ybt1.txt 
	Two lines of test text 
	Three text Two text
	#匹配空行并排除
	[root@ybt ybt]# sed -n '/^$/!p' ybt1.txt 
	One line of test text 
	Two lines of test text 
	Three text Two text
	
#反转文本文件
	[root@ybt ybt]# cat ybt.txt	源文件
	111111111111111
	222222222222222
	333333333333333
	#反转文本
	[root@ybt ybt]# sed -n '{1!G;h;$p}' ybt.txt			#很难理解,先记住
	333333333333333
	222222222222222
	111111111111111
	
用tac命令反转文本文件 #刚好是cat的反写
	[root@ybt ybt]# tac ybt.txt
	333333333333333
	222222222222222
	111111111111111

b 分支
	[address]b [label]
	address决定了哪些行的数据会触发分支命令。label参数定义了要跳转到的位置。如果没有加label参数,跳转命令会跳转到脚本的结尾。
	[root@ybt ybt]# cat ybt1.txt 
	One line of test text 
	Two lines of test text 
	Three text Two text
	four line test  text
	#1,2行触发分支命令,没有label参数,命令自动跳转到脚本结尾
	[root@ybt ybt]# sed '{1,2b ; s/text/ybt/}' ybt1.txt 
	One line of test text 
	Two lines of test text 
	Three ybt Two text
	four line test  ybt
	#将标签定位到four并将text替换为空其他text替换为ybt
	[root@ybt ybt]# sed '{/four/b tz ; s/text/ybt/g
	:tz
	s/text//g}' ybt1.txt
	One line of test ybt 
	Two lines of test ybt 
	Three ybt Two ybt
	four line test
	
	#每次迭代替换一个,号
	[root@ybt ybt]# echo 'ybt,ke,hello,world' |sed -n '{
	:st
	s/,/ /1p
	b st		#到这个位置又跳到了标签st,形成了无线循环
	}'
	ybt ke,hello,world
	ybt ke hello,world
	ybt ke hello world
	...#无线循环了
	
	#每次迭代替换一个,号
	[root@ybt ybt]# echo 'ybt,ke,hello,world' |sed -n '{
	:st
	s/,/ /1p
	/,/b st	#为分支命令指定一个地址模式来查找,如果没有跳转就结束;
	}'
	ybt ke,hello,world
	ybt ke hello,world
	ybt ke hello world

t 测试
	#测试命令会根据替换命令的结果跳转到某个标签,而不是根据地址进行跳转
	#如果替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签。如果替换命令未能匹配指定的模式,测试命令就不会跳转。
	#命令格式[address]t [label];在没有指定标签的情况下,如果测试成功,sed会跳转到脚本的结尾
	[root@ybt ybt]# cat ybt1.txt 	源文件
	One line of test text 
	Two lines of test text 
	Three text Two text
	four line test  text
	#第一个替换查找One。如果匹配,它就替换,测试命令会跳过后面的替换。如果第一个替换未能匹配,第二个替换就会被执行。
	[root@ybt ybt]# sed '{
	> s/One/ybt/
	> t
	> s/text/110/
	> }' ybt1.txt
	ybt line of test text 
	Two lines of test 110 
	Three 110 Two text
	four line test  110
	#用测试执行分支无线循环
	[root@ybt ybt]# echo 'ybt,ke,hello,world' |sed -n '{
	:st
	s/,/ /1p
	t st	#跳回st标签后,如果替换命令未能匹配指定的模式,测试命令就不会跳转。
	}' 
	ybt ke,hello,world
	ybt ke hello,world
	ybt ke hello world

模式替换
& 符号	#	&符号会提取匹配替换命令中指定模式的整个字符串。
	#.可以匹配任意字符,但是替换后的文本无法复制
	[root@ybt ybt]# echo ybt ais bis |sed 's/.is/".is"/g'
	ybt ".is" ".is"
	#用&就可以了
	[root@ybt ybt]# echo ybt ais bis |sed 's/.is/"&"/g'
	ybt "ais" "bis"
	#在以下情况不能用&,因为会匹配到整个模式
	[root@ybt ybt]# echo ybt ais bis |sed -r 's/ais (.is)/\1/g'   #不加-r 就需要在括号前加转义,告诉系统是分组符号,不过-r就可以解决
	ybt bis
	
	

sed使用工具


	[root@ybt ybt]# cat a3	源文件
	
	ybt
	
	kexiao
	
	hello world
	
删除除了区间外的空行
	[root@ybt ybt]# sed '/./,/^$/!d' a3
	ybt
	
	kexiao
	
	hello world
	
删除开头空行
	[root@ybt ybt]# sed '/./,$!d' a3
	ybt

	kexiao

	hello world
 
删除结尾空行
	[root@ybt ybt]# sed '{			#在脚本的花括号里还有花括号。允许你在整个命令脚本中将一些命令分组
	: s
	/^\n*$/{$d;N;b s}
	}' a3
	
	ybt
	
	kexiao
	
	hello world
	#该命令组会被应用在指定的地址模式上。地址模式能够匹配只含有一个换行符的行。如果找到了这样的行,而且还是最后一行,删除命令会删掉它。如果不是最后一行,N命令会将下一行附加到它后面,分支命令会跳到循环起始位置重新开始。

awk

awk也是一个编辑器 #不过强大了一点
	#书本写的是gawk  但是看了gawk和awk的帮助后。发现不能说太像,只能说一模一样
命令格式
	gawk options program file
awk选项
	-F fs 指定行中划分数据字段的字段分隔符
	-f file 从指定的文件中读取程序
	-v var=value 定义gawk程序中的一个变量及其默认值
	-mf N 指定要处理的数据文件中的最大字段数
	-mr N 指定数据文件中的最大数据行数
	-W keyword 指定gawk的兼容模式或警告等级
	
	$1 $2 取第几列信息
	$NF 取最后一列
	$(NF-n)取倒数第几列
	$0 取所有列信息

	从命令行读取程序脚本
		#awk程序脚本用一对花括号来定义。你必须将脚本命令放到两个花括号({})中。
		#awk中默认的字段分隔符是任意的空白字符
只显示第一列的值
	[root@ybt ybt]# cat ybt.txt 
	111111111111111
	222222222222222
	333333333333333

	[root@ybt ybt]# awk -F "" '{print $1}' ybt.txt   #-F指定分隔符,awk默认空格为分隔
	1
	2
	3

替换
	[root@ybt ybt]# echo "one hello world" |awk  '{$1="two" ; print $0}'
	two hello world

从文件读取命令
	[root@ybt ybt]# cat ke1
	{$1="two" ; print $0}		#也可以把“;”号去掉写成两行
	[root@ybt ybt]# awk -F "" -f ke1 ybt.txt 	#-F指定分隔符
	two 1 1 1 1 1 1 1 1 1 1 1 1 1 1
	two 2 2 2 2 2 2 2 2 2 2 2 2 2 2
	two 3 3 3 3 3 3 3 3 3 3 3 3 3 3

加入开始标题和结束标题 #开始BEGIN 结束END
	[root@ybt ybt]# awk 'BEGIN {print "hello world"}{print $0}END{print "end"}' ybt.txt 
	hello world
	111111111111111
	222222222222222
	333333333333333
	end


开始结束awk示例
	[root@ybt ybt]# cat beginend.sh 	
	BEGIN{ 						#所有“;”号都省略了
	print "第一列\t最后一列"
	print "___________________"
	FS=""							#FS定义分隔符
	}
	
	{
	print $1 "\t" $2
	}

	END{
	print "结束"
	}
	
	[root@ybt ybt]# awk -f beginend.sh ybt.txt 	 使用awk输出
	第一列	最后一列
	___________________
	1	1
	2	2
	3	3
	结束

进阶awk

支持变量
内建变量	
	FIELDWIDTHS 由空格分隔的一列数字,定义了每个数据字段确切宽度
	FS 输入字段分隔符
	RS 输入记录分隔符
	OFS 输出字段分隔符
	ORS 输出记录分隔符
		#默认情况下,awk将RS和ORS设为换行符。
	[root@ybt ybt]# cat a4	源文件
	ybt1,ybt2,ybt3,ybt4
	ybt1,ybt2,ybt3,ybt4
	#设置输出分隔符为--
	[root@ybt ybt]# awk 'BEGIN{FS=",";OFS="--"}{print $1,$2,$3}' a4		#-F ","
 	ybt1--ybt2--ybt3
	ybt1--ybt2--ybt3
	
	[root@ybt ybt]# cat a5 源文件
	ybt 
	15538
	
	kexiao
	17737
	#将RS设置为空白符,将默认输入分隔符设置为\n
	[root@ybt ybt]# awk 'BEGIN{FS="\n";RS=""}{print $1,$2}' a5
	ybt  15538
	kexiao 17737
	#一旦设定了FIELDWIDTHS变量的值,就不能再改变了(不实用于长变量)	#设置了FIELDWIDTH变量,gawk就会忽略FS变量
	[root@ybt ybt]# cat ybt.txt		源文件
	111111111111111
	222222222222222
	333333333333333
	[root@ybt ybt]# awk 'BEGIN{FIELDWIDTHS="2 3 2" }{print $1,$2,$3}' ybt.txt
	11 111 11
	22 222 22
	33 333 33

	ARGC和ARGV变量允许从shell中获得命令行参数的总数以及它们的值
	[root@ybt ybt]# awk 'BEGIN{print ARGC,ARGV[1]}' a5
	2 a5
	[root@ybt ybt]# awk 'BEGIN{print ARGV[1]}' a5	#引用参数不用加$
	a5
	[root@ybt ybt]# awk 'BEGIN{print ARGC,ARGV[0]}' a5		
	2 awk				#0就是awk命令了
	
	#ENVIRON变量提取环境变量的值
	[root@ybt ybt]# awk 'BEGIN{print ENVIRON["HOME"]}'
	/root
	
	#NR和FNR类似,但略微不同;NR显示最后一个字段的值,FNR变量含有当前数据文件中已处理过的记录数,
	[root@ybt ybt]# cat a1	源文件
	This is the header line. 
	This is the first data line. 
	This is the second data line. 
	This is the last line.
	#查看header行的最后一个字段值
	[root@ybt ybt]# awk '/header/{print $NF}' a1
	line.
	#NR可以查看单行信息,也可以查看范围行信息
	[root@ybt ybt]# awk 'NR==1' a1		
	This is the header line. 
	[root@ybt ybt]# awk 'NR==1,NR==3' a1
	This is the header line. 
	This is the first data line. 
	This is the second data line. 
	
	#NR于FNR区别:,FNR的值会在处理每个数据文件时被重置,而NR的值则会继续计数直到处理完所有的数据文件。
	[root@ybt ybt]# cat a6		源文件
	1
	2
	3
	[root@ybt ybt]# awk '{print $1,"FNR:"FNR,"NR:"NR}' a6 a6
	1 FNR:1 NR:1
	2 FNR:2 NR:2
	3 FNR:3 NR:3
	1 FNR:1 NR:4
	2 FNR:2 NR:5
	3 FNR:3 NR:6

	
自定义变量
	#awk自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头。变量名区分大小写。
	
	#对ybt进行赋值并输出
	[root@ybt ybt]# awk 'BEGIN{ybt="This is a test" ;print ybt}' 
	This is a test
	#也可以赋值运算,包括求余符号(%)和幂运算符号(^或**)
	[root@ybt ybt]# awk 'BEGIN{y=5*2+3; print y}'
	13
	[root@ybt ~]# awk 'BEGIN{y=10;y=y*100;print y}'
	1000

	#在命令行上给变量赋值
	[root@ybt ybt]# cat a8
	BEGIN{FS=" "}		#FS值必须定义
	{print $n}				#
	[root@ybt ybt]# cat ybt1.txt 
	One line of test text
	Two lines of test text
	Three text Two text
	four line test  text
	[root@ybt ybt]# awk   -f a8 n=3  ybt1.txt 
	of
	of
	Two
	test
	
	#用-v命令行参数,允许你在BEGIN代码之前设定变量
	[root@ybt ybt]# awk -v n=3 -f a8 ybt1.txt 
	of
	of
	Two
	test

	
数组变量==python中的字典
	#命令格式
	var[index] = element
	#设置数组变量ybt[1]="hello world"
	[root@ybt ybt]# awk 'BEGIN{ybt[1]="hello world"; print ybt[1]}' 
	hello world
	#使用数组变量做运算
	[root@ybt ybt]# awk 'BEGIN{ybt[1]="50";ybt[2]="10"; print ybt[1]*ybt[2]}' 
	500
	[root@ybt ~]# awk 'BEGIN{ybt[1]="50";ybt[2]="10"; s=ybt[1]*ybt[2];print s}' 
	500


遍历数组变量
	#命令格式
	for (var in array) 
	{ 
	 statements 
	}
	示例
	[root@ybt ybt]# awk 'BEGIN{
	y1["a"]=1
	y1["b"]=2
	y1["c"]=3
	for (ybt in y1)
	{
	print ybt,y1[ybt]
	}
	}'
	a 1
	b 2
	c 3		#结论:变量保存了index值

删除数组变量
	命令格式
	delete array[index]
	delete y1["a"]		#使用遍历例子
	[root@ybt ~]# awk 'BEGIN{
	y["1"]=10
	y["2"]=20
	for (ybt in y)
	{
	print ybt,y[ybt]
	}
	delete y["2"]
	print "---"
	for (ybt in y)
	print ybt,y[ybt]
	}'
	1 10
	2 20
	---
	1 10


~ 匹配操作符 #可以指定匹配操作符、数据字段变量以及要匹配的正则表达式。
	[root@ybt ybt]# cat ybt1.txt 	源文件
	One line of test text
	Two lines of test text
	Three text Two text
	four line test  text
	#匹配第二个字段是text的行,并输出它的整行
	[root@ybt ybt]# awk '$2~/text/{print $0}' ybt1.txt 
	Three text Two text
	#匹配第二个字段是text的行,并输出它第一个字段
	[root@ybt ybt]# awk '$2~/text/{print $1}' ybt1.txt 
	Three
	#匹配第二个字段不是text的行,并输出它第一个字段
	[root@ybt ybt]# awk '$2!~/text/{print $1}' ybt1.txt 
	One
	Two
	four

数学表达式
	x == y:值x等于y。 
	x <= y:值x小于等于y。 
	x < y:值x小于y。
	x >= y:值x大于等于y。
	x > y:值x大于y。
    
    #显示所有属于root用户组(组ID为0)的系统用户
	[root@ybt ybt]# awk -F ":" '$4 ==0{print $1}' /etc/passwd
	root
	sync
	shutdown
	halt
	operator

	#直接指定文本
	[root@ybt ybt]# awk  '$2=="lines"{print $0}' ybt1.txt
	Two lines of test text
	[root@ybt ~]# awk '$1~"Tw*"{print $0}' ybt1.txt
	Two lines of test text 
	Three text Two text


if 语句#如果条件求值为TRUE,紧跟在if语句后的语句会执行。如果条件求值为FALSE,这条语句就会被跳过
	命令格式
		if (condition) 
 		statement1 
 		或 if (condition) statement1
 	[root@ybt ybt]# cat b1	源文件
	10
	20
	30
	40
	50
	#判断>30的输出
	[root@ybt ybt]# awk '{if ($1 >30) print $1}' b1
	40
	50
	#在if语句中执行多条语句,就必须用花括号将它们括起来
 	[root@ybt ybt]# awk '{
	if ($1>20)
	{x = $1*2 ;print x}		#判断print输出的内容
	}' b1
	60
	80
	100
	
fi else 语句
	语法
	if (condition) statement1; else statement2
	[root@ybt ybt]# awk '{
	if($1>20)
	{print $1*2}
	else 
	{print $1/2}
	}' b1
	5
	10
	60
	80
	100
	#单行模式
	[root@ybt ybt]# awk '{if ($1>20) print $1*2; else print $1/2}' b1
	5
	10
	60
	80
	100

while 语句
	语法格式
		while (condition) 
		{ 
		 statements 
		}
	示例
	[root@ybt ybt]# cat b2	源文件
	1 2 3
	4 5 6
	7 8 9
	#计算文件中每一行的平均数
	[root@ybt ybt]# awk '{
	ybt =0
	i =1
	while (i<4)
	{
	  ybt +=$i		#while遍历每一行互不干扰,一行值相加,直到遍历完整个文件
	  i++
	}
	avg =ybt /3		
	print avg
	}' b2
	2
	5
	8
while break	语句
	#当i==2时终止循环
	[root@ybt ybt]# awk '{
	ybt=0
	i=1
	while (i<4)
	{
	 ybt +=$i
	 if (i==2)	
	 break
	 i++
	}
	avg=ybt/2
	print avg
	}' b2
	1.5
	4.5
	7.5

do-while 语句
	语法格式
		do 
		{ 
		 statements 
		} while (condition)
	[root@ybt ybt]# cat b2
	1 2 3
	4 5 6
	7 8 9
	#当不小于5时就输出;这种格式可以保证条件被求值之前至少执行一次
	[root@ybt ybt]# awk '{
	ybt=0
	i=1
	do
	{
	 ybt +=$i
	 i++
	}
	while (ybt<5)
	print ybt}' b2
	6
	9
	7
	
for 语句
	#awk支持C风格的for循环
	for( variable assignment; condition; iteration process)
	[root@ybt ybt]# cat b2
	1 2 3
	4 5 6
	7 8 9
	#求每行平均数
	[root@ybt ybt]# awk '{
	> ybt =0
	> for (i=1;i<4;i++)
	> {
	>   ybt +=$i
	> }
	> avg=ybt/3
	> print avg
	> }' b2
	2
	5
	8
	
格式化打印
	命令格式
		printf "format string", var1, var2 . . .
		#format string会用文本元素和格式化指定符来具体指定如何呈现格式化输出
		%[modifier]control-letter #control-letter是一个单字符代码,用于指明显示什么类型的数据
	格式化指定符的控制字母
		c 将一个数作为ASCII字符显示
		d 显示一个整数值
		i 显示一个整数值(跟d一样)
		e 用科学计数法显示一个数
		f 显示一个浮点值
		g 用科学计数法或浮点数显示(选择较短的格式)
		o 显示一个八进制值
		s 显示一个文本字符串
		x 显示一个十六进制值
		X 显示一个十六进制值,但用大写字母A~F
	#格式化打印一个16进制的数
		[root@ybt ybt]# awk 'BEGIN{
		x=10*10
		printf "%X\n",x			#printf默认输出不换行
		}'
		64
	3种修饰符进一步控制输出
		width:指定输出字段最小宽度的数字值,如果输出短于这个值,printf会将文本右对齐,并用空格进行填充如果输出比指定的宽度还要长,则按照实际的长度输出
		prec:是一个数字,指定浮点数小数位数,或者文本字符串中显示的最大字符数
		-减号:指定格式化空间放入数据采用左对齐
	#使用左对齐和指定宽度16
		[root@ybt ybt]# awk '{printf "%-16s %d\n" ,$1,$2}' a1
		ybt              2978
		kexiao           17737
		gouzi            379
	#使用f指定小数位
		[root@ybt ybt]# cat b2
		1 2 3
		4 5 6
		7 8 9
		[root@ybt ybt]# awk '{
		ybt=0
		for (i=1;i<4;i++)
		{
		  ybt +=$i
		}
		awg=ybt/3
		printf "%0.2f\n",awg	#0.2指定两位小数,整数部分是文本字符串中显示的最大字符数(左边宽度)
		}' b2
		2.00
		5.00
		8.00

awk函数

数学函数
	atan2(x, y) x/y的反正切,x和y以弧度为单位
	cos(x) x的余弦,x以弧度为单位
	exp(x) x的指数函数
	int(x) x的整数部分,取靠近零一侧的值
	log(x) x的自然对数
	rand( ) 比0大比1小的随机浮点值
	sin(x) x的正弦,x以弧度为单位
	sqrt(x) x的平方根
	srand(x) 为计算随机数指定一个种子值
	示例
	[root@ybt ybt]# awk 'BEGIN{x=int(10 * rand()) ;x=exp(100);print x}'
	26881171418161356094253400435962903554686976

字符串函数	#更多p489
	tolower(s) 将s中的所有字符转换成小写
	toupper(s) 将s中的所有字符转换成大写
	index(s, t) 返回字符串t在字符串s中的索引值,如果没找到的话返回0
	length([s]) 返回字符串s的长度;如果没有指定的话,返回$0的长度
	示例
	[root@ybt ybt]# awk 'BEGIN{x="yBt";print tolower(x) ; print toupper(x)}'
	ybt
	YBT
	[root@ybt ~]# awk 'BEGIN{x="ybt";print index(x,"b")}'
	2
	[root@ybt ~]# awk 'BEGIN{x="ybt";print length(x)}'
	3
	[root@ybt ~]# awk 'BEGIN{print length("ybt")}'
	3

时间函数
	mktime(datespec) 将一个按YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳值①
	strftime(format [,timestamp]) 将当前时间的时间戳或timestamp(如果提供了的话)转化格式化日期(采用shell函数date()的格式)
	systime( ) 返回当前时间的时间戳	#1970年0101到现在的秒数
	示例
	[root@ybt ybt]#  awk 'BEGIN{
	> date = systime()
	> day = strftime("%A, %B %d, %Y", date)
	> print day
	> }'
	星期三, 三月 30, 2022

自定义函数
	命令格式 
	function name([variables]) 	#要定义自己的函数,必须用function关键字。
	{ 
	 statements 		#在定义函数时,它必须出现在所有代码块之前(包括BEGIN代码块)。
	}
	示例
	[root@ybt ybt]# cat a1	源文件
	  ybt 2978
	kexiao  17737
	gouzi   0379
	#定义函数并执行
	[root@ybt ybt]# awk '
	function ybt()
	{
	    printf "%-10s  %d\n", $1,$2
	}
	BEGIN{FS=" "}
	{
	    ybt()
	}' a1
	ybt         2978
	kexiao      17737
	gouzi       379

创建函数库
	#创建完函数库后,就可以在所有的awk程序使用了
	[root@ybt ybt]# cat mingling1 	
	BEGIN{FS=" "}		#创建命令引用函数
	{
	    ybt()
	}
	#创建awk函数文件
	[root@ybt ybt]# cat hanshu1 	
	function ybt()
	{
	    printf "%-10s  %d\n", $1,$2
	}
	[root@ybt ybt]# awk -f mingling1 -f hanshu1 a1	#-f执行
	ybt         2978
	kexiao      17737
	gouzi       379

  

正则表达式

#可对awk,sed,grep直接使用

正则表达式识别的特殊字符包括:
	.*[]^${}\+?|() #记住不能在文本模式中单独使用这些字符,想要在文本中放入需要转义(当作文本来匹配)
	
	#正斜线不是正则表达式的特殊字符,但如果它出现在sed编辑器或gawk程序的正则表达式中,你就会得到一个错误
	[root@ybt ybt]# cat zhengze.txt  源文件
	/ ybt ke 
	[root@ybt ybt]# sed -n '///p' zhengze.txt 
	sed:-e 表达式 #1,字符 3:未知的命令:“/”
	[root@ybt ybt]# sed -n '/\//p' zhengze.txt   #转移后正常,总结就是文本中特殊字符,需要转义
	/ ybt ke 

^ 脱字符从数据流中文本行的行首开始的模式。
	#以什么开头的信息进行匹配
	[root@ybt ybt]# cat yt	源文件
	chou gou zi ni shi shei na ge ^gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	gou
	
	#匹配每行开头为y的行
	[root@ybt ybt]# sed -n  '/^y/p' yt
	yang bi tian and zhan ke xiao
	
	#将脱字符放到模式开头之外的其他位置,那么它就跟普通字符一样,不再是特殊字符了
	[root@ybt ybt]# sed -n '/e ^/p' yt
	chou gou zi ni shi shei na ge ^gou
	
	#如果指定正则表达式模式时只用了脱字符,就不需要用反斜线来转义。但如果你在模式中先指定了脱字符,随后还有其他一些文本,那么你必须在脱字符前用转义字符。
	#匹配文本中有^gou的行
	[root@ybt ybt]# sed -n '/^gou/p' yt
	gou
	[root@ybt ybt]# sed -n '/\^gou/p' yt
	chou gou zi ni shi shei na ge ^gou

	
	
$ 定义了行尾锚点。
	#已什么结尾的信息进行匹配
	[root@ybt ybt]# cat yt	源文件
	chou gou zi ni shi shei na ge ^gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	gou
	#匹配结尾是sha的行
	[root@ybt ybt]# sed -n '/sha$/p'  yt	
	hello world bu zhi dao xin sha

^$ 组合匹配空行
	[root@ybt ybt]# cat yt	源文件
	chou gou zi ni shi shei na ge ^gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	gou
	#精确匹配
	[root@ybt ybt]# sed -n '/^yang bi tian and zhan ke xiao$/p' yt
	yang bi tian and zhan ke xiao
	#匹配空行
	[root@ybt ybt]# sed -n '/^$/p' yt

. 点符号用来匹配除换行符之外的任意单个字符	#且只有一个字符
	[root@ybt ybt]# cat dian	源文件
	at
	cat
	lcat
	 at
	a t
	#匹配单个字符和at组合的行
	[root@ybt ybt]# sed -n '/.at/p' dian
	cat			#完美匹配
	lcat		#匹配到了cat
	 at			#在正则表达式中,空格也是字符
[] 字符组
	#[0-9]+匹配多个数字;[0-9] 匹配单个数字;加号表明必须有至少一个字符;  #+号是扩展正则
	#[a-zA-Z0-9]找出所有以小写字母大写字母和数字的字符
	#[a-Z]找出所有小写字母或大写字母组成字符
	#[a-ch-m] 匹配a-c和h-m的字母
	[root@ybt ybt]# cat zifuzu 	源文件
	1
	2
	12
	123
	13
	23
	#匹配有1或者2的行
	[root@ybt ybt]# sed -n '/[12]/p' zifuzu 
	1
	2
	12
	123
	13
	23
	#匹配有123 13 23 的行
	[root@ybt ybt]# sed -n '/[12]3/p' zifuzu 
	123
	13
	23
	#示例2
	[root@ybt ybt]# cat zifuzu2 源文件
	123
	3456
	456
	#匹配有三个数字组成的任意行
	[root@ybt ybt]# sed -n '/[0-9][0-9][0-9]/p' zifuzu2
	123
	3456	#显然这上面有前三位数字,符合三位数字组成的任意行
	456
	#精确匹配开头是任意数字结尾是任意数字的“三位数”的行
	[root@ybt ybt]# sed -n '/^[0-9][0-9][0-9]$/p' zifuzu2 
	123
	456
	

	[root@ybt ybt]# cat yt		源文件
	chou gou zi ni shi shei na ge ^gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	gou
	[root@ybt ybt]# sed -n '/^[a-Z][a-Z][a-Z]$/p' yt
	gou
	
BRE特殊字符组		
	[[:alpha:]] 匹配任意字母字符,不管是大写还是小写
	#[[:alnum:]] 匹配任意字母数字字符0~9、A~Z或a~z 
	#[[:blank:]] 匹配空格或制表符
	[[:digit:]] 匹配0~9之间的数字
	[[:lower:]] 匹配小写字母字符a~z 
	#[[:print:]] 匹配任意可打印字符
	[[:punct:]] 匹配标点符号
	#[[:space:]] 匹配任意空白字符:空格、制表符、NL、FF、VT和CR 
	[[:upper:]] 匹配任意大写字母字符A~Z
	
	示例
	[root@ybt ybt]# sed -n '/^[[:alnum:]][[:alnum:]][[:alnum:]]$/p' yt
	gou
	


[^]排除行字符组
	[root@ybt ybt]# cat zifuzu2
	123
	3456
	456
	#排除3456的行如果有未被匹配的字符,整行不会被排除
	[root@ybt ybt]# sed -n '/[^3456]/p' zifuzu2
	123
	#没有匹配到3,所以3456行也没有被排除
	[root@ybt ybt]# sed -n '/[^456]/p' zifuzu2
	123
	3456
	
*星号;匹配前一个字符连续出现了0次或多次
	[root@ybt ybt]# cat xinghao 
	ybt
	ybbt
	ybbbt
	cybta
	#匹配所有有y*t的行
	[root@ybt ybt]# sed -n '/y*t/p' xinghao 
	ybt
	ybbt
	ybbbt
	cybta


.*匹配任意数量任意字符
	[root@ybt ybt]# cat yt		源文件
	chou gou zi ni shi shei na ge ^gou
	hello world bu zhi dao xin sha
	yang bi tian and zhan ke xiao
	gou
	[root@ybt ybt]# sed -n  '/yan.*xiao/p' yt
	yang bi tian and zhan ke xiao

扩展正则

awk直接使用
	默认grep sed不能直接识别
	grep提升egrep或grep -E
	sed提升sed -r
	
? 匹配前一个字符出现0或者1次
	[root@ybt ybt]# echo "ybt"| awk '/yb?t/{print $0}'
	ybt
	[root@ybt ybt]# echo "ybt"| awk '/y?t/{print $0}'
	ybt
	[root@ybt ybt]# echo "ybbt"| awk '/y[b]?t/{print $0}'
	[root@ybt ybt]# 		#什么也没有输出,只匹配前一个字符出现0或者1次

	[root@ybt ybt]# echo "ybt"|sed -rn '/y?t/p' 	#加上-r同样实现
	ybt

+ “前面”的字符可以出现1次或多次,但必须至少出现1次
	#[0-9]+匹配单个或多个连续数字,加号表明必须有至少一个字符;
	[root@ybt ybt]# cat 123	#查看源文件
	123
	12
	1
	#匹配单个字符
	[root@ybt ybt]# egrep '[1-9]' 123 -o	#-o参数显示过程
	1
	2
	3
	1
	2
	1
	#匹配多个字符
	[root@ybt ybt]# egrep '[1-9]+' 123 -o
	123
	12
	1
	
	[root@ybt ybt]# cat zifuzu #查看源文件
	1
	2
	12
	123
	13
	23
	#匹配有23连续的行
	[root@ybt ybt]# sed -nr '/23+/p' zifuzu
	123
	23
	#匹配有12连续的行
	[root@ybt ybt]# sed -nr '/12+/p' zifuzu
	12
	123
	#匹配有123连续的行
	[root@ybt ybt]# sed -rn '/123+/p' zifuzu
	123
	#匹配有1 13 3 的行
	[root@ybt ybt]# sed -nr '/[13]+/p' zifuzu
	1
	12
	123
	13
	23
	#没有4或5无法匹配
	[root@ybt ybt]# sed -nr '/1[45]3+/p' zifuzu
	


{}可以指定字符连续匹配的次数
	x{n,m} 表示匹配x至少出现n次,最多出现m次
	#必须指定awk程序的--re- interval命令行选项才能识别正则表达式间隔。 #CenotOs7测试不加没问题
	#匹配b最少出现一次最多出现2次
	[root@ybt ybt]# echo ybt|awk  '/yb{1,2}t/{print $0}'
	ybt
	[root@ybt ybt]# echo ybbt|awk  '/yb{1,2}t/{print $0}'
	ybbt
	[root@ybt ybt]# echo ybbbt|awk  '/yb{1,2}t/{print $0}'
	#匹配b最少出现2次
	[root@ybt ybt]# echo ybt |sed -nr '/yb{2,}t/p'
	[root@ybt ybt]# echo ybbt |sed -nr '/yb{2,}t/p'
	ybbt
	#匹配b最少出现2次最多4次
	[root@ybt ybt]# echo ybbbbt |sed -nr '/yb{2,4}t/p'
	ybbbbt
	[root@ybt ybt]# echo ybbbbbt |sed -nr '/yb{2,4}t/p'
	
| 管道符号 并且或符号,用于匹配多个信息
	#查看有one或cat的行
	[root@ybt ybt]# echo ybt one fist|sed -nr '/one|cat/p' 
	ybt one fist
	[root@ybt ybt]# echo ybt cat fist|sed -nr '/one|cat/p' 
	ybt cat fist
	[root@ybt ybt]# echo ybt cat fist|awk '/one|cat/{print $0}'
	ybt cat fist
	[root@ybt ybt]# echo ybt one fist|awk '/one|cat/{print $0}'
	ybt one fist
	
() 圆括号分组。
	#当你将正则表达式模式分组时,该组会被视为一个标准字符。可以像对普通字符一样给该组使用特殊字符
	#匹配ybt的one出现0次或一次
	[root@ybt ybt]# echo ybtone|awk '/ybt(one)?/{print $0}'
	ybtone
	[root@ybt ybt]# echo ybt|awk '/ybt(one)?/{print $0}'
	ybt
	#匹配ybt ybT Ybt YbT
	[root@ybt ybt]# echo ybt|awk '/(y|Y)b(T|t)/{print $0}'
	ybt
	
	进行后项引用前项使用:sed命令替换功能
		#将数字分为三组只引用第一组和第三组,并将第三组加上<>
		[root@ybt ybt]# echo 123456|sed -r 's/(..)(..)(..)/\1<\3>/g'
		12<56>

正则实战

题目:验证美国电话号码

美国号码格式为:
		(123)456-7890 
		(123) 456-7890 
		123-456-7890 
		123.456.7890
脚本示例
	[root@ybt ybt]# cat yanzhengphone.sh 
	#!/bin/bash
	#验证美国电话号码
	phone=`mktemp -t YBT.XXXX`
	ybt(){
	read -n1 -p "是否继续查询:y/n" ke
		case "$ke" in
		Y|y)
			echo ;;
		N|n)
			echo
			exit;;
		*)
			ybt;;
		
		esac
	}
	
	while [ 1 ] ;do
		clear
		read -p "验证你的号码是否是美国号码请输入:" tbphone
		echo "$tbphone" > "$phone"
		kexiao=`awk '/^\(?[0-9]{3}\)? ?[0-9]{3}-[0-9]{4}$/' "$phone"`
		kexiao1=`awk '/^[0-9]{3}-[0-9]{3}-[0-9]{4}$/' "$phone"`
		kexiao2=`awk '/^[0-9]{3}\.[0-9]{3}\.[0-9]{4}$/' "$phone"`
		if [  "$kexiao"  ] ;then
			echo "$tbphone 是美国号码"
			ybt
		elif	[  "$kexiao1"  ] ;then
	
			 echo "$tbphone 是美国号码"
	                ybt
	        elif    [  "$kexiao2"  ] ;then
	
	                 echo "$tbphone 是美国号码"
	                ybt
			
		else		
			echo 不是美国号码
			 ybt
		fi
	done
	
	脚本执行
	验证你的号码是否是美国号码请输入:(123) 456-7890
	(123) 456-7890 是美国号码
	是否继续查询:y/ny
	验证你的号码是否是美国号码请输入:123-456-7890
	123-456-7890 是美国号码
	是否继续查询:y/n

脚本实战

userdel.sh

[root@ybt ybt]# cat userdel.sh
#!/bin/bash

#用户输入超时提醒与自动退出函数ybt1
ybt1(){
zz=0
#-z检测变量长度是否为0,不为0退出
while [ -z "$ybt" ];do	
		echo -e "\n$ss"
	read  -t 4 -p "请输入你要删除的用户名:" ybt
		case "$zz" in 
		1)
			echo -e "\n \n 第一次未输入或超时提醒\n";;
		2)
			echo -e "\n \n 第二次未输入或超时提醒\n";;
		3)
			echo -e "\n \n 第三次未输入或次超时,程序即将退出\n"
			exit;;
		esac
		zz=$[ $zz+1 ]
done
}


#判断继续或返回函数ybt2
#把continue和break写到函数跳出或外边的循环shell独一份了  ;不建议这样写
ybt2(){
while [ 1 ];do
	case "$ke" in
		y|Y|yes|YES)
		break	;;
		n|N|no|NO)	
			continue 2;;
		*)
		read -p "请输入[y/n]:" ke
		
	esac 
done	
}

#主程序
while [ 1 ];do
	clear
	unset ybt
	ss="---------------------------------------"
	ybt1

	#1.判断用户是否为空
	if [ -n "$ybt" ];then
	read -t 60 -p "确定要删除 “$ybt” [y/n]:" ke
		ybt2
	else	
		echo -en "\n输入用户名为空,回车继续:"
		read
		continue
	fi
	
	#2.判断用户是否真实存在	 
	#不存在grep退出状态会变成1
	echo "$ss"
	user=`awk -F ":" '{print $1}' /etc/passwd |grep -w $ybt`
	if [ "$?" -eq 0  ];then
		echo -e "\n检查“$ybt” 是否存在"
		echo -e "......“$ybt” 用户真实存在\n"
		read -t 60 -p "确定要删除 “$ybt” [y/n]:" ke
		ybt2
        else
                echo -en "\n输入账户不存在,回车继续"
                read
		continue
        fi

 	#3.查看进程信息,让用户选择是否删除进程
	#如果ps命令的退出状态码返回了1,那么表明系统上没有属于该用户账户的进程在运行。
	#但如果退出状态码返回了0,那么系统上有属于该账户的进程在运行。
	ps -u $ybt  &> /dev/null 
	if  [ $? -eq 0 ]  ;then
		echo "$ss"
		echo -e "\n“$ybt” 用户的进程为"
		ps -u $ybt
		echo -en "\n你想清除该用户的进程吗 [y/n]:"
		read ke
		ybt2
		ps -u $ybt | awk '{print $1}' | xargs  kill -9 &> deleteuser.log
		ps -u $ybt
                echo "“$ybt” 进程已删除"
	else
		echo "“$ybt” 用户没有进程" 
		fi	

	#4.查找要删除的用户,并询问是否备份他的文件
	echo "$ss"
	echo -en "\n 是否要备份 “$ybt” 的文件[y/n]:"
	read ke
		ybt2
		find / -user $ybt  &> ybt.txt
		sed -i '/^find/d' ybt.txt ; tar -zcf ybt.tar.gz `cat find.txt` &> /dev/null
		echo "“$ybt” 备份文件名字为 $ybt.tar.gz"
	
	#5.删除用户
	echo "$ss"
	userdel -r $ybt
	echo  "“$ybt” 已删除"
	id $ybt
	echo "$ss"
	read -p "任意键继续,或n退出:" ke
	case "$ke" in 
		n|N|no|NO)
	        exit;;        
		*)
		continue;;
	esac
done

mysql

cat /etc/my.cnf
将ybt设置为免密
	[client]	
	password=ybt

脚本示例
	[root@ybt ybt]# cat mysql.sh
	#!/bin/bash
	my=$(which mysql) 
	$my  ybt -u  ke  <<EOF #ke为表名 ybt为库名
	show tables;
	select * from ke;
	select $1,$2 from ke;
	EOF
	
		
	
	[root@ybt ybt]# ./mysql.sh empid firstname	脚本执行
	Tables_in_ybt
	ke
	empid	lastname	firstname	salary
	1	Blum	Rich	25000
	2	Blum	Barbara	45000
	empid	firstname
	1	Rich
	2	Barbara


	#-B指定mysql程序工作在批处理模式运行,-s用于禁止输出列标题和格式化符号。-e是linux命令行输入
	[root@ybt ybt]# cat ./mysql1.sh 
	#!/bin/bash
	my=`which mysql`
	
	ybt=`$my ybt -u ke -Bse 'show databases'`
	for i in "$ybt";do
		echo $i;done
		
	[root@ybt ybt]# ./mysql1.sh 	#脚本查询了所有库
	information_schema mysql performance_schema sys ybt

	#-X实现;支持可扩展标记语言XML的格式。这种语言使用和HTML类似的标签来标识数据名和值。
posted @ 2022-04-22 21:57  ybt&kexiao  阅读(73)  评论(0编辑  收藏  举报