测试
test 条件
条件为真返回 0,条件为假返回 1
[ 条件 ]
test 能够理解3种类型的表达式
1.文件测试
2.字符串比较
3.数字比较
字符串
-n STRING
# -n 字符串长度不为零
-z STRING
# -z 字符串长度为0
STRING1 = STRING2
# = 判断两个字符串是否一样
STRING1 != STRING2
# != 判断两个字符串是否不一样
数字
eq 等于 ne 不等于 #
ge 大于等于 le 小于等于 #
gt 大于 lt 小于 #
文件
test
-f 存在且是普通文件 # 重要
-d 存在且是目录 #
-h 存在且是符号链接
-b 块设备
-c 字符设备
-e 文件存在 #
流控制:
•在一个shell脚本中的命令执行顺序称作脚本的流。大多数脚本会根据一个或多个条件来改变它们的流。
•流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令
•exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出,例如:
•exit 0
条件判断
If代码返回0表示真,非0为假
if语句语法如下: 1
if [ $1 -eq 1 ]
then
echo '等于1'
elif [ $1 -eq 2 ]
then
echo '等于二'
else
echo '既不等一,也不等于二'
fi
例:
#!/bin/bash
read -p "请输入号码: " num
if [ $num = 1 ];then
echo "1"
elif [ $num = 2 ];then
echo "2"
else
echo "输入有误!"
fi
例:脚本if.sh,必须在脚本后加上适当的参数脚本才能正确执行
#!/bin/bash
if [ "$1" = "hello" ]; then
echo "Hello! How are you ?"
elif [ "$1" = "" ]; then
echo "You MUST input parameters"
else
echo "The only accept parameter is hello"
fi
练习(选做):
1)检测apache是否运行,如果没有运行则启动,并记录启动的时间,保存到日志中。
2)测试ip地址主机位从2到100的机器是否存活,并把存活的机器记录到文本文件alivehost.txt内。(使用ping命令)
多个条件联合
逻辑与
if [ $condition1 ] && [ $condition2 ]
if [ $condition -a $condition2 ]
if [[ $condition1 && $condition2 ]]
逻辑或
if [ $condition1 ] || [ $condition2 ]
if [ $condition -o $condition2 ]
if [[ $condition1 || $condition2 ]]
# test 和 [] 中 我们可以使用 [ $condition1 ] && [ $condition2 ] 或者 [ $condition -a $condition2 ]
# 在 [[]] 这种情况,我们可以直接使用[[ $condition1 && $condition2 ]]
建议在if中直接使用[[]]这种方式,这种方式更加稳定。[[]] shell的一个命令。
-a && 逻辑与 and 两端的条件都可以成立
-o || 逻辑或 or 两端的条件有一段成立就行
练习:
编写脚本port.sh,执行脚本后显示系统的httpd、ftp、ssh、sendmail这些服务是否开启
case
case 语句是 shell 中流控制的第二种方式,语法如下:
case $word in
pattern1)
list1
;;
pattern2)
list2
;;
patternN)
listN
;;
*)
list*
;;
esac
read -p "请输入你的数字: " number
# case $number in
# 1)
# echo "任务1"
# ;;
# 2)
# echo "任务2"
# ;;
# 3|4|5)
# eval " echo 任务'$number'还没有开发完成"\
# ;;
# *)
# echo "输入错误"
# esac
if [ $number -eq 1 ];then
:
elif [ $number -eq 2 ];then
:
elif [ $number -eq 3 -o $number -eq 4 -o $number -eq 5 ];then
:
else
echo "error"
fi
命令;;表明流应该跳转到case语句的最后,类似C语言中的break指令。
练习:建立脚本case.sh,当执行时,要求我们在键盘输入适当的值(one|two|three),当输入正确时并打印,当输入错误 时会提示你,应该输入正确的值。
case $var in
模式1)
执行1
;;
模式2)
执行2
;;
模式3)
执行3
;;
*)
执行4
esac
第一行: 声明case关键字调用case语法, 紧跟的“变量”一般为用户的输入值, in代表从下方的各个模式进行匹配
第2-4行: 匹配到“模式1”后进行命令的输出或执行, 模式1: 一般为字符或数值
第11-12行: 当用户输入的字符不存在匹配模式时, 直接执行或打印*)下的命令或语句
示例:
[root@bavdu shell_scripts]# vim system_tools
#!/usr/bin/env bash
cat <<-EOF
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
EOF
echo "Please input your select: " && read var
case "$var" in
"a")
systemctl stop firewalld && systemctl disable firewalld
;;
"b")
setenforce 0
;;
"c")
yum -y install httpd httpd-tools
;;
"d")
exit
;;
*)
printf "请按照上方提供的选项输入!!!\n"
;;
esac
for i in {取值范围} # for 关键字 i 变量名 in 关键字 取值范围格式 空格 制表符 换行
do # do 循环体的开始
循环体
done # done 循环体的结束
#!/usr/bin/env bash
#
# Author:
# Date: 2019/**/**
for i in {1..100}
do
echo $i
done
#!/bin/bash # 类似c语言的循环方式
for (( i=1;i <= 5;i++ ))
do
echo "$i"
done
测试生产环境的主机存活性
#!/usr/bin/env bash #
# Author: #{}& 并发
>ip_alive.txt # 在执行脚本之前,先将文件清空
>ip_down.txt
segment="192.168.161"
for i in {2..254}
do
{
ping -c1 $segment.$i &>/dev/null
if [ $? -eq 0 ];then
printf "alive: $segment.$i\n" >>ip_alive.txt
else
printf "down: $segment.$i\n" >>ip_down.txt
fi
}&
done
wait #等待循环结束后,执行下面操作
echo "finish..."
for循环批量创建用户
#!/bin/bash
while : #死循环
do
read -p "请设置用户前缀/数量/密码: " prefix num pass
cat <<-EOF # 打印到屏幕
用户前缀:$prefix
用户数量:$num
用户密码:$pass
EOF
read -p "是否确认创建:[Y/N]" action
if [ $action = Y ];then
"starting create users..."
break
fi
done
for i in `seq -w $num`
do
user=$prefix$i
id $user &>/dev/null
if [ $? -eq 0 ];then
echo "$user is already exist"
else
useradd $user
echo $pass | passwd --stdin $user &>/dev/null # 用户密码设置不需要交互
fi
done
while 条件 # while 关键字 条件 [ $1 -lt 10 ] ,while循环,条件为真的情况下,会循环
do
循环体
done
# 计数器
a=0
while [ $a -lt 100 ]
do
# a=$[ $a + 1 ]
let a++
echo $a
done
a=0
while read line
do
let a++
echo "$a $line"
done < /etc/passwd
完善系统工具的输出及操作性
创建一个文件里面的用户
#!/bin/bash
while read user
do
id $user &>/dev/null
if [ $? -eq 0 ];then
echo "$user is already exists"
else
useradd $user
echo "create $user successfully"
fi
done < user.txt
#!/usr/bin/env bash
#
# Author:
while 1>0
do
cat <<-EOF
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
EOF
echo " Please input your select: " && read var
case "$var" in
"a")
systemctl stop firewalld && systemctl disable firewalld
;;
"b")
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
;;
"c")
yum -y install httpd httpd-tools
;;
"d")
exit
;;
*)
echo "请按照上方提供的选项输入!!!"
;;
esac
if [ $? -eq 0 ];then
clear
else
echo "Warning: Your program exist ERROR!!!"
break
fi
done
练习题:
输出用户输入的参数,直到用户输入 "end" 结束循环
#!/usr/bin/bash
while 2>1
do
read -p "请输入: " word
if [ $word != 'end' ];then
echo "$word"
else
echo "退出循环"
break
fi
done
until 条件 # 当后面的条件表达式,为假的时候进行循环,当他为真了就停止循环了。
do
循环体
done
a=0
until [ $a -gt 10 ]
do
let a++
echo $a
done
[root@newrain ~]# cat until.sh
#!/bin/bash
x=1
until [ $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
x=1
while [ ! $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
#shift命令 将位置参数左移
位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢 弃,$0不移动。不带参数的shift命令相当于shift 1。
对于位置变量或命令行参数,其个数必须是确定的,或者当 Shell 程序不知道其个数时,可以把所有参数一起赋值给变量 $*。
若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等,则需要用shift把所有参数变成$1
#测试 shift 命令(x_shift3.sh)
[root@newrain shell]# cat x_shift3.sh
#!/bin/bash
shift
echo "第一个位置参数: $1"
[root@newrain shell]# bash x_shift3.sh 2 3
第一个位置参数: 3
#测试 shift 命令(x_shift.sh)
#!/bin/bash
until [ $# -eq 0 ]
do
echo "第一个参数为: $1 参数个数为: $#"
shift
done
执行以上程序x_shift.sh:
$./x_shift.sh 1 2 3 4
结果显示如下:
第一个参数为: 1 参数个数为: 4
第一个参数为: 2 参数个数为: 3
第一个参数为: 3 参数个数为: 2
第一个参数为: 4 参数个数为: 1
从上可知 shift 命令每执行一次,变量的个数($#)减一,而变量值提前一位
用 until 和 shift 命令计算所有命令行参数的和。
#shift 上档命令的应用(x_shift2.sh)
sum=0
until [ $# -eq 0 ]
do
sum=`expr $sum + $1`
shift
echo "sum is: $sum"
执行上述程序:
$x_shift2.sh 10 20 15
其显示结果为:
45
continue、break、exit命令
Linux脚本中的break continue exit return
break
结束并退出本次循环
continue
在循环中不执行continue下面的代码,转而进入下一轮循环
exit
退出脚本
常带一个整数给系统,如 exit 0
可理解为:break是立马跳出循环;continue是跳出当前条件循环,继续下一轮条件循环;exit是直接退出整个脚本
例如:
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。
break命令
break命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
代码如下:
#!/bin/bash
while :
do
echo -n "请输入1-5之间的数字 "
read aNum
case $aNum in
1|2|3|4|5)
echo "您的数字是 $aNum!"
;;
*) echo "您输入的数字不再1-5中间,游戏结束!"
break
;;
esac
done
continue命令
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
对上面的例子进行修改:
代码如下:
#!/bin/bash
while :
do
echo -n "请输入1-5之间的数字 "
read aNum
case $aNum in
1|2|3|4|5)
echo "您的数字是 $aNum!"
;;
*)
echo "您输入的数字不再1-5中间,游戏结束!";、
continue
;;
esac
done
运行代码发现,当输入大于5的数字时,该例中的循环不会结束.
break和exit的区别
[root@newrain shell]# cat case07.sh
#!/bin/bash
while true
do
read -p "请输入[1/2]" num1
case $num1 in
1)
echo $num1
;;
2)
while true
do
read -p "再次输入[1/2]:" num2
case $num2 in
1) echo $num2;;
2) break; #将此处换成exit,再次尝试
esac
done
esac
done
ps:整理自己的思路,完善不足的地方
#!/usr/bin/env bash
#
# Author:
#可以先添加上账密验证环节
while : # 死循环
do
#trap ':' INT EXIT TSTP TERM HUP # 拒绝 ctrl+c ctrl+d 其他的退出方式
clear
cat <<-EOF
+-------------------------------------+
| JumpServer @Version1.0 |
+-------------------------------------+
| a. WebServer Apache. |
| b. MySQL Databases Server. |
| c. PHP Development Computer. |
| d. Quit |
+-------------------------------------+
EOF
read -p "请输入你要登录的服务器编号: " computer
case $computer in
a)
ssh [email protected] # 可以嵌套子case循环
;;
b)
ssh [email protected]
;;
c)
ssh [email protected]
;;
d)
exit
;;
*)
printf "ERROR: Please redo your select!"
;;
esac
done
function (功能) 功能函数
完成特定功能的代码片段
函数必须先定义才能使用
优点:避免重复的代码
定义函数 1 怎么定义 2 定义什么东西
调用函数 1 可以本地调用,也可以调用别的脚本的函数 2 在不调用之前,它相当于没有
取消函数 1 把函数名想象成一个变量名
函数传参 1 跟脚本传参很类似
命名空间 1 在shell语言中命名空间函数内和函数外是一致的,函数内外不能赋值同样名字的变量
local 1 声明一个本地变量(局部变量)
a=10
func(){
local a=20
echo $a
}
func
返回值
return value
value不能超过0-255
function 声明函数
check 函数的名字
() 没有实际意义
{} 函数体
function check() {
echo $1
}
check $1
函数声明
function_name () {
list of commands
}
函数名 function_name,这就是你将使用它从其他地方在你的脚本调用。
取消函数
unset myfunc //取消函数
myfunc() //函数定义
{
echo “This is my first shell function”
}
myfunc //函数调用
function check(){
if [ $1 != $USER ];then
echo "当前登录的用户不是$1"
else
echo "当前登录的用户是$1"
fi
}
check $1
ip_list="www.baidu.com www.taobao.com www.123.com 192.168.100.1"
function check(){
for i in $@
do
ping -W1 -c1 $i &>/dev/null
if [ $? -eq 0 ];then
echo "$i可以访问"
else
echo "$i不可以访问"
fi
done
}
check $ip_list
产生以下执行结果
./test.sh
This is my first shell function
函数必须提前定义测试
[root@newrain fun]# cat fun05.sh
#!/bin/bash
fun () {
echo "hello"
}
fun
unset fun
fun
[root@newrain fun]# bash fun05.sh
hello
fun05.sh: line 8: fun: command not found
函数的返回值,返回的是函数体内最后一条命令是否成功的返回值
[root@newrain fun]# systemctl stop httpd
[root@newrain fun]# cat fun03.sh
#!/bin/bash
fun() {
systemctl status httpd &>/dev/null
systemctl status vsftpd &>/dev/null
}
fun
echo $?
[root@newrain fun]# systemctl stop vsftpd
[root@newrain fun]# bash fun03.sh
3
函数传参
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数
示例
[root@newrain fun]# cat fun06.sh
#!/bin/bash
fun() {
echo $[$1*$2*$3]
}
fun 1 2 3
修改版:
[root@newrain fun]# cat fun06.sh
#!/bin/bash
if [ ! $# -eq 3 ];then
echo "Must Input Three number: p1 p2 p3"
exit fi
fun() {
echo $[$1*$2*$3]
}
fun $1 $2 $3
返回值
func(){
ping -W1 -c1 www.baidu.com &>/dev/null
if [ $? -eq 0 ];then
return 0
else
ping -W1 -c1 8.8.8.8 &>/dev/null
if [ $? -eq 0 ];then
return 10
else
ping -W1 -c1 192.168.100.2 &>/dev/null
if [ $? -eq 0 ];then
return 20
else
return 30
fi
fi
fi
}
func
result=$?
if [ $result -eq 0 ];then
echo "网络正常"
elif [ $result -eq 10 ];then
echo "DNS配置异常"
elif [ $result -eq 20 ];then
echo "当前网络环境异常"
else
echo "当前主机网络异常"
fi
普通数组:只能用整数作为数组的索引(一维数组) 、多维数组
关联数组:可以使用字符串作为数组的索引
普通数组定义:
[root@newrain shell]# books=( linux shell awk sed )
取值:
[root@newrain shell]# echo ${books[0]}
linux
[root@newrain shell]# echo ${books[1]}
shell
[root@newrain shell]# echo ${books[2]}
awk
[root@newrain shell]# echo ${books[-1]}
sed
另外的定义方式:
books=( [0]=linux [1]=shell [2]=awk [3]=sed )
取出所有的元素
echo ${books[@]}
取出所有的索引
echo ${!books[@]}
取出索引的个数
echo ${#books[@]}
在数组中增加元素
books[4]="mysql"
修改数组中的某个值
books[0]="grep"
删除数组中的某个元素
unset books[0]
切片
echo ${books[@]:2:3}
多维数组了解:
declare -A p_list
p_list=([0,1]=zhangsan [0,2]=lisi [1,1]=wangwu [1,2]=zhaoliu)
echo ${p_list[@]}
echo ${p_list[0,2]}
关联数组需要提前声明
declare -A myarry1
[root@newrain shell]# declare -A myarry1
[root@newrain shell]# myarry1=([name]=newrain [sex]=man [age]=26) //无序的 切片
[root@newrain shell]# echo ${myarry1[name]}
newrain
[root@newrain shell]# echo ${myarry1[age]}
26
定义方法1:
# declare -a myarry=(5 6 7 8)
# echo ${myarry[2]}
显示结果为 7
定义方法2:
# array=( one two three four five six )
# array2=(tom jack alice)
# array3=(`cat /etc/passwd`)
# array4=(tom jack alice "bash shell")
# array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack)
定义方法3: # 普通数组下标只能是数字
#!/bin/bash
area[11]=23
area[13]=37
area[51]="UFOs"
#数组贴片
array=( one two three four five six )
echo ${array[@]:1:3}#取一到三之间的数值
#步长
当设置任何数组变量时,可以访问它
[root@newrain shell]# aa=(haha heihei baibai)
[root@newrain shell]# echo ${aa[0]} //访问数组中的第一个元素
[root@newrain shell]# echo ${aa[@]} //访问数组中所有的元素,等同与echo ${aa[*]}
[root@newrain shell]# echo ${#aa[@]} //统计元素的个数
[root@newrain shell]# echo ${!aa[@]} //统计索引
${array_name[index]} //引用
示例
#!/bin/bash
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
NAME[5]="ZZ"
echo "First Index: ${NAME[0]}"
echo "Second Index: ${NAME[1]}"
echo "sixth Index: ${NAME[5]}"
输出结果为
$./test.sh
First Index: BJ
Second Index: SH
sixth Index: ZZ
您可以访问数组中的所有项目通过以下方式之一:
${array_name[*]}
${array_name[@]}
示例
#!/bin/sh
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
echo "First Index: ${NAME[*]}"
echo "Second Index: ${NAME[@]}"
输出结果
$./test.sh
First Index: BJ SH SZ GZ HZ
Second Index: BJ SH SZ GZ HZ
关于在shell脚本中数组变量中 “*”跟 “@” 区别
“*”当变量加上“” 会当成一串字符串处理.
“@”变量加上“” 依然当做数组处理.
在没有加上“” 的情况下 效果是等效的.
示例
#!/usr/bin/env bash
array=(gz cloud 19)
echo "case 1"
for line in "${array[@]}"
do
echo $line
done
echo "case 2"
for line in "${array[*]}"
do
echo $line
done
echo "case 3"
for line in ${array[*]}
do
echo $line
done
echo "case 4"
for line in ${array[@]}
do
echo $line
done
执行结果
case 1
gz
cloud
19
case 2
gz cloud 19
case 3
gz
cloud
19
case 4
gz
cloud
19
遍历数组while
[root@newrain array]# cat array01.sh
#!/bin/bash
#++ i 是先自加1后赋值;i ++ 是先赋值后自加1。
while read line
do
host[i++]=$line # 观察i++ 和 ++i的区别
done </etc/hosts
for i in ${!host[@]} # 数组的元素索引
do
echo "$i:${host[i]}"
done
遍历数组for
[root@newrain array]# cat array02.sh
#!/bin/bash
IFS=''
for line in `cat /etc/hosts` # 读取文件中的每一行
do
host[j++]=$line
done
for i in ${!host[@]}
do
echo ${host[i]}
done
以上两个脚本都是读取文件中的行,然后加到一个数组中并进行遍历。
练习题:统计shell的种类和数量
思路:最后一列的sh种类不同,我们可以单独取出最后一列 /etc/passwd
正则表达式基本元字符
正则表达式拓展元字符
用来处理文本
正则表达式(Regular Expression, RE)是一种字符模式, 用于在查找过程中匹配指定的字符. 在大多数程序里, 正则表达式都被置于两个正斜杠之间;
例如/l[oO]ve/就是由正斜杠界定的正则表达式, 它将匹配被查找的行中任何位置出现的相同模式. 在正则表达式中,元 字符是最重要的概念
元字符使正则表达式具有处理能力。所谓元字符就是指ß那些在正则表达式中具有特殊意义的专用字符,可以用来规定 其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
基本正则表达式元字符
元字符
示例 功能
^ 行首定位符
^love
$ 行尾定位符
love$
. 匹配单个字符
l..e
* 匹配前导符0到多次 全部匹配 grep "o*" /etc/passwd
ab*love
.* 匹配任意多个字符 (贪婪匹配
[] 匹配方括号中任意一个字符
[lL]ove
[ - ] 匹配指定范围内的一个字符
[a-z0-9]ove
[^] 匹配不在指定组里的字符
[^a-z0-9]ove
\ 用来转义元字符
love\.
\< 词首定位符 #由数字或字母组成的
\<love
\> 词尾定位符
love\>
\(\) 匹配后的标签 # 在vim中测试
:%s@\(张三\) \(李四\) \(王五\)@ \3 \1 \2@g
= 等于 != 不等于 =~ 匹配
扩展正则表达式元字符
+ 匹配一个或多个前导字符
[a-z]+ove
? 匹配零个或一个前导字符
lo?ve
"r.*t" 贪婪匹配
-P "r.*?" 非贪婪匹配
a|b 匹配a或b
love|hate
() 组字符loveable|rs
love(able|rs) ov+ ov+ (ov)+
(oo)+ 两个oo出现一次或者多个
(..)(..)\1\2 标签匹配字符 #
(love)able\1er
x{m} 字符x重复m次
o{5}
o{,3} 字符最多出现3次
x{m,} 字符x重复至少m次
o{5,} 字符最少出现5次
x{m,n} 字符x重复m到n次
o{5,10}
grep -E "o{3}" /etc/passwd
grep -P "\s" /etc/passwd
grep -P "\d" /etc/passwd
egrep 支持正则表达式的拓展元字符 (或grep -E)
#egrep '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' file1.txt
[root@newrain ~]# num1=1
1、运用正则,判断需要[[ ]]
[root@newrain ~]# [[ $num1 =~ ^[0-9]+$ ]] && echo "yes" || echo "no"
yes
[root@newrain ~]# num3=1b1
[root@newrain ~]# [[ $num3 =~ ^[0-9]+$ ]] && echo "yes" || echo "no"
no
[root@newrain ~]# [[ $num =~ ^[0-9]+\.[0-9]+$ || $num =~ ^[0-9]+$ ]] && echo "yes" || echo "no" //输入的只能是数字(包括小数)
2、* 0或多个
[root@newrain ~]# useradd abrt
[root@newrain ~]# grep 'abc*' /etc/passwd
abrt:x:1041:1041::/home/abrt:/bin/bash
3、\< 词首定位符号 \>词尾定位符号
[root@newrain ~]# cat jack.txt
Jack JACK JAck jackly
:% s/\<[Jj]ack\>/123/g
4、^以什么开头
[root@newrain ~]# grep '^root' /etc/passwd
root:x:0:0:root:/root:/bin/bash
5、$以什么结尾
[root@newrain ~]# grep 'bash$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
confluence:x:1000:1000:Atlassian Confluence:/home/confluence:/bin/bash
to:x:1003:1003::/home/to:/bin/bash
6、. 匹配单个字符
[root@newrain ~]# grep 'r..t' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[root@newrain ~]# grep 'r.t' /etc/passwd
operator:x:11:0:operator:/root:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
7、.* 任意多个字符
[root@newrain ~]# grep 'r.*t' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin polkitd:x:999:997:User for polkitd:/:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
abrt:x:1041:1041::/home/abrt:/bin/
8、[] 匹配方括号中的任意一个字符
[root@newrain ~]# grep 'Root' /etc/passwd
[root@newrain ~]# grep '[Rr]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
9、[ - ] 匹配指定范围内的一个字符
[root@newrain ~]# grep [a-z]oot /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
10、[^] 匹配不在指定组内的字符,取反得意思
[root@newrain ~]# grep '[^0-9]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[root@newrain ~]# grep '[^0-9A-Z]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[root@newrain ~]# grep '[^0-9A-Za-z]oot' /etc/passwd
[root@newrain ~]#
[root@newrain ~]# useradd Root
[root@newrain ~]# grep '[a-z]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin Root:x:1042:1042::/home/Root:/bin/bash
[root@newrain ~]# grep '^[rc]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
^在[]内表示取反,^在[]外表示以什么开头
11、\(\)匹配后的标签
[root@newrain ~]# cat file1.txt
IPADDR=192.168.1.123
GATEWAY=192.168.1.1
NETMASK=255.255.255.0
DNS=114.114.114.114
:% s#\(192.168.1.\)123#\12#
:% s#\(192.\)\(168.\)\(1.\)2#\1\2\35#
:% s\(192.\)\(168.\)\(1.\)\(5\)#\1\26.\4#
扩展正则表达式元字符
+ 匹配一个或多个前导字符
[a-z]+ove
? 匹配零个或一个前导字符
lo?ve
a|b 匹配a或b
love|hate
(..)(..)\1\2 标签匹配字符
(love)able\1er
x{m} 字符x重复m次
o{5
x{m,} 字符x重复至少m次
o{5,}
x{m,n} 字符x重复m到n次
o{5,10}
1、+ 匹配一个或多个前导字符
[root@newrain ~]# egrep 'ro+t' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
2、? 匹配零个或一个前导字符
[root@newrain ~]# egrep 'ro?t' /etc/passwd
abrt:x:1041:1041::/home/abrt:/bin/bash
3、a|b 匹配a或b
[root@newrain ~]# netstat -anlp|egrep ':80|:22'
[root@newrain ~]# egrep 'root|alice'
/etc/passwd root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
4、x{m} 字符x重复m次
[root@newrain ~]# cat a.txt
love
love.
loove
looooove
[root@newrain ~]# egrep 'o{2}' a.txt
loove
looooove
[root@newrain ~]# egrep 'o{2,}' a.txt
loove
looooove
[root@newrain ~]# egrep 'o{6,7}' a.txt
非交互式编辑器,一次处理一行内容。(流文本编辑器)
sed "参数" '模式'
#参数:
-f 指定一个规则文件
-n 阻止输入行输出
-r 扩展正则
#模式:
s 替换
g 整行(也可以是数字,替换第几个)
d 删除
p 打印 r
a 追加
i 是插入
示例文件
file1.txt
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA
用Massachusetts替换MA: #前面两个斜杠中是要匹配的内容,可以使用正则,后面两个斜杠中间,是要替换的内容,是纯文本
#sed 's/MA/Massachusetts/' file1.txt
使用多重指令:
# sed 's/MA/Massachusetts/ ; s/PA/Pennsylvania/' file1.txt
使用脚本文件:
脚本:namestate
s/MA/Massachusetts/
s/PA/Pennsylvania/
s/CA/California/
s/VA/Virginia/
s/OK/Oklahoma/
-f<script文件>或--file=<script文件> 以选项中指定的script文件来处理输入的文本文件。
$ sed -f namestate file1.txt
保存输出:
$ sed -f namestate file1.txt > newfile.txt
阻止输入行自动显示:
$ sed -n 's/MA/Massachusetts/p' file1.txt
sed: stream editor(流编辑器)的缩写. 它们最常见的用法是进行文本的替换.
[root@newrain ~]# sed '1d' passwd //删除文件的第1行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@newrain ~]# sed '1,2d' passwd //删除文件的第1到2行 daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@newrain ~]# cat e.txt
/etc/abc/456
etc
[root@newrain ~]# sed -r 's#/etc/abc#/var/lib#' e.txt
/var/lib/456
etc
[root@newrain ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@newrain ~]# sed '2,$d' passwd //删除第2行到最后一行s
root:x:0:0:root:/root:/bin/bash
[root@newrain ~]# sed '/root/d' passwd //匹配到root,删除此行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@newrain ~]# sed '/root/,2d' passwd //匹配到root行,到某一行
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@newrain ~]# cat -n passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
[root@newrain ~]# sed '1~2d' passwd //删除奇数行
bin:x:1:1:bin:/bin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
operator:x:11:0:operator:/root:/sbin/nologin
[root@newrain ~]# sed '0~2d' passwd //删除偶数行
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
sed可以从stdin中读取内容
$ cat filename | sed 's/pattern/replace_string/'
选项 -i 会使得sed用修改后的数据替换原文件
$ sed -i 's/pattern/replace_string/' filename
g标记可以使sed执行全局替换
$ sed 's/pattern/replace_string/g' filename
$ sed 's/pattern/replace_string/gi' filename //忽略大小写替换
g标记可以使sed匹配第N次以后的字符被替换
$ echo "thisthisthisthis" | sed 's/this/THIS/2g'
sed中的分隔符可以替换成别的字符, 因为s标识会认为后面的字符为分隔符
$ sed 's:text:replace_text:'
$ sed 's|text|replace_text|'
sed可以利用指令来删除文件中的空行
$ sed '/^$/d' filename
由于在使用 -i 参数时比较危险, 所以我们在使用i参数时在后面加上.bak就会产生一个备份的文件,以防后悔
sed -i.bak 's/pattern/replace_string/' filename
sed如果在脚本中使用的话, 不可避免的要调用变量, 所以以下这种方式可以用来调用变量即’ '换成了" "
$ text=hello
$ echo "hello world" | sed "s/$text/HELLO/"
在文件中匹配到的部分前后加上一行
sed '/^bin/a\hello nihao/' passwd # 在匹配到开头为bin的行下一行插入内容
sed '/^bin/i\hello nihao/' passwd # 在匹配到开头为bin的行上一行插入内容
BENGIN{} 在文本处理之前要做的事情,只执行一次
{} 在处理文本时做的事情,文本有多少行,就执行多少次
END{} 在文本处理结束之后做的事情,只执行一次
F 指定分割符
awk中的变量
$0 一整行
$1 第一个字段
NF 列的总数
$NF 最后一列
NR 行号
FNR 按照文件区分行号
FS 字段的分割符
OFS 输出字段分隔符
RS 输入记录分隔符
ORS 输出记录分隔符
awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息
awk处理过程: 依次对每一行进行处理,然后输出 默认分隔符是空格或者tab键
awk 参数 'BEGIN{处理之前要做的} {处理内容} END{处理之后的内容}'
BEGIN{} {}
行处理前
END{}
行处理 行处理后
[root@newrain ~]# awk 'BEGIN{print 1/2} {print "ok"} END{print "----"}' /etc/hosts # 打印的内容加上双引号
0.5
ok
ok
ok
ok
ok
ok
----
awk工作原理
awk -F":" '{print $1,$3}' /etc/passwd
(1)awk使用一行作为输入,并将这一行赋给变量$0,每一行可称作为一个记录,以换行符结束
(2)然后,行被:分解成字段,每个字段存储在已编号的变量中,从$1开始
(3)awk如何知道空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符,初始时,FS赋为空格或者是tab
(4)awk打印字段时,将以设置的方法,使用print函数打印,awk在打印的字段间加上空格,因为$1,$3间有一个,逗号。逗 号比较特殊,映射为另一个变量,成为输出字段分隔符OFS,OFS默认为空格
(5)awk打印字段时,将从文件中获取每一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程持续到处理文件结束。
默认分隔符是空格或者tab键
awk中的特殊变量:
常用:
- NR: 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号
- NF: 表示字段数量, 当awk将行为记录时, 该变量相当于当前列号
难理解:
FS(输入字段分隔符) # 以什么符号去分割
OFS(输出字段分隔符) # 以什么分隔符显示
NR(Number of record)行数
FNR按不同的文件分开
RS(输入记录分隔符)
ORS(输出记录分隔符)
NF 字段个数
FS(输入字段分隔符) (filed sign)
[root@newrain ~]# awk 'BEGIN{FS=":"} {print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
OFS(输出字段分隔符) (output filed sign)
[root@newrain ~]# awk 'BEGIN{FS=":";OFS=".."} {print $1,$2}' /etc/passwd =
root..x
bin..x
daemon..x
adm..x
lp..x
sync..x
shutdown..x
NR 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号
[root@newrain ~]# awk -F: '{print NR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
6 isuo
7 IPADDR=192.168.6.5
8 hjahj123
9 GATEWAY=192.168.1.1
10 NETMASK=255.255.255.0
11 DNS=114.114.114.114
FNR 表示记录编号, 当awk将行为记录时, 该变量相当于当前行号(不同文件分开)
[root@newrain ~]# awk -F: '{print FNR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
1 isuo
2 IPADDR=192.168.6.5
3 hjahj123
4 GATEWAY=192.168.1.1
5 NETMASK=255.255.255.0
6 DNS=114.114.114.114
RS(输入记录分隔符)
awk 'BEGIN{RS="好"}{print $1}' ping.txt
我是门城
啦啦啦啦啦绿
[root@newrain ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@newrain ~]# awk -F: 'BEGIN{RS="bash"} {print $0}' passwd
root:x:0:0:root:/root:/bin/
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
ORS(输出记录分隔符)
[root@newrain ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@newrain ~]# awk -F: 'BEGIN{ORS=" "} {print $0}' passwd
root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
练习:将文件合并为一行
[root@newrain ~]# awk 'BEGIN{ORS="" } {print $0}' /etc/passwd
练习:把一行内容分为多行
[root@newrain ~]# cat d.txt
root:x:0:0:root:/root:/bin/bash
[root@newrain ~]# awk 'BEGIN{RS=":"} {print $0}' d.txt
root
x
0
0
root
/root
/bin/bash
打印一个文件中的第2列和第3列
$ awk '{ print $2, $3}' filename
打印指定行指定列的某个字符
$ awk -F":" 'NR==3{ print $7 }' /etc/passwd
统计一个文件的行数
$ awk '{ print NR}' filename
在脚本中, 传递变量到awk中
$ var=1000
$ echo | awk -v VARIABLE=$var '{ print VARIABLE }'
$ awk -F: '{ print $2, $3 }' filename
$ awk 'BEGIN{ FS=":" }{ print $2, $3 }' filename
每行打印两次
[root@newrain ~]# awk -F: '{for(i=1;i<=2;i++) {print $0}}' passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
分别打印每行每列
[root@newrain ~]# awk -F: '{ for(i=1;i<=NF;i++) {print $i}}' passwd
显示管理员用户名
[root@newrain ~]# awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd
统计系统用户
[root@newrain ~]# awk -F":" '{if($3>0 && $3<1000){i++}} END{print i}' /etc/passwd
[root@newrain ~]# yum -y install expect
是一个免费的编程工具, 用来实现自动的交互式任务, 而无需人为干预. 说白了 expect 就是一套用来实现自动交互功能的软件
在实际工作中我们运行命令、脚本或程序时, 这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输 入都需要人为的手工进行. 而利用 expect 则可以根据程序的提示, 模拟标准输入提供给程序, 从而实现自动化交互执 行. 这就是 expect
能够在工作中熟练的使用Shell脚本就可以很大程度的提高工作效率, 如果再搭配上expect,那么很多工作都可以自动化 进行,对工作的展开如虎添翼
用法:
1)定义脚本执行的shell
#!/usr/bin/expect 类似于#!/bin/bash
2)set timeout 30
设置超时时间30s
3)spawn
spawn是进入expect环境后才能执行的内部命令,不能直接在默认的shell环境中执行
功能:传递交互命令
4)expect
这里的expect同样是expect命令
功能:判断输出结果是否包含某项字符串,没有则立即返回,否则等待一段时间后返回,等待通过timeout设置
5)send
执行交互动作,将交互要执行的动作进行输入给交互指令
命令字符串结尾要加上“r”,如果出现异常等待状态可以进行核查
6)interact
执行完后保持交互状态,把控制权交给控制台
如果不加这一项,交互完成会自动退出
7)exp_continue
继续执行接下来的操作
expect环境中设置变量用set,识别不了bash方式定义的变量
错误方式:
[root@newrain expect]# cat expect02.sh
#!/usr/bin/expect
user=22
spawn echo $user
[root@newrain expect]# ./expect02.sh
invalid command name "user=22"
while executing
"user=22"
(file "./expect02.sh" line 3)
正确方式:
[root@newrain expect]# cat ./expect02.sh
#!/usr/bin/expect
set user 22
spawn echo $user
[root@newrain expect]# ./expect02.sh
spawn echo 22 ============================================================================
[root@newrain expect]# cat expect01.sh
#!/usr/bin/expect
spawn ssh [email protected]
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "123\r" }
}
interact
interact的作用测试
执行测试是否免交互:
要是用/usr/bin/expect的shell执行
[root@newrain expect]#/usr/bin/expect expect01.sh
成功
擦除最后一行interact进行测试
[root@newrain expect]#/usr/bin/expect expect01.sh
进入之后立即退出 ============================================================================
\r的作用测试
[root@newrain expect]# cat expect01.sh
#!/usr/bin/expect
set user root
set pass 123
set ip 192.168.62.146
spawn ssh $user@$ip
expect {
"yes/no" { send "yes";exp_continue }
"password:" { send "$pass" }
}
interact
[root@newrain expect]# ./expect01.sh
spawn ssh [email protected]
[email protected]'s password:
加上\r之后
[root@newrain expect]# ./expect01.sh
spawn ssh [email protected]
[email protected]'s password:
Permission denied, please try again.
[email protected]'s password: ============================================================================
设置变量的方式
[root@newrain expect]# cat expect01.sh #!/usr/bin/expect
set user root
set pass 123
set ip 192.168.62.146
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
interact
============================================================================
设置位置参数的方式(拓展)
[root@newrain expect]# cat expect01.sh
#!/usr/bin/expect
set timeout 30
set user [ lindex $argv 0 ]
set pass [ lindex $argv 1 ]
set ip [ lindex $argv 2 ]S
spawn ssh $user@$ip
expect {
"yes/no" { send "yes";exp_continue }
"password:" { send "$pass\r" }
}
interact
运行结果为:
[root@newrain expect]# ./expect01.sh root 123 192.168.62.146
spawn ssh [email protected]
[email protected]'s password:
Last login: Thu Aug 15 23:26:59 2019 from 192.168.62.136
#!/usr/bin/expect
set username hello # 定义变量
set passwd 1
spawn ssh $username@172.17.138.107 # 执行交互式命令
expect { # 捕捉系统返回,与系统进行交互
"yes/no" { send "yes\r";exp_continue }
"password:" {send "${passwd}\r"}
}
expect "*]$" # 捕捉系统返回,与系统进行交互
send "touch /tmp/abcaaa\r"
send "ls /\r"
expect eof
interact