Bash shell学习笔记(七)

学习目标

  • 能够在awk操作中应用运算符
  • 了解sed的分域操作

awk的运算符

再次强调,awk是一门语言,所以有自己的运算符(注意:有些地方与shell不一样)

运算符说明
==等于 和shell里不一样,shell里字符串比较是= ;数字比较是-eq
!=不等于 shell里数字比较是-ne代表不等于
>大于 shell里数字比较是(-gt)代表大于
<小于 shell里数字比较是(-lt)代表小于
>=大于等于 shell里数字比较是(-ge)代表大于等于
<=小于等于 shell里数字比较是(-le)代表小于等于
&&逻辑与(和) [条件1 -a 条件2]
||逻辑或 [条件1 -o 条件2]
+加法
-减法
*乘法
/除法
%求余数

通过运算符和NR内部变量的结合可以控制处理文本的行

打印/etc/passwd第五行(可以把NR==5看作是一个判断的条件,满足此条件才执行print $0)

# awk 'NR==5 {print $0}' /etc/passwd

打印/etc/passwd第五到十行,并在前面加上行号

# awk 'NR>=5 && NR<=10 {print NR,$0}' /etc/passwd

打印第五行和第六行

# awk -F: 'NR==5 || NR==6 {print $0}' /etc/passwd

打印/etc/passwd奇数行 (删除偶数行)

# awk 'NR%2==1 {print NR,$0}' /etc/passwd

打印/etc/passwd偶数行 (删除奇数行)

# awk 'NR%2==0 {print NR,$0}' /etc/passwd

对/etc/passwd里的用户做分类,分成管理员,系统用户,普通用户

# awk -F: '$3==0 {print $1}' /etc/passwd

# awk -F: '$3<500 && $3>0 || $3==65534  {print $1}' /etc/passwd

# awk -F: '$3>499 && $3!=65534 {print $1}' /etc/passwd

练习:

打印/etc/passwd前五行

# head -5 /etc/passwd
# sed -n '1,5p' /etc/passwd
# awk 'NR<=5 {print $0}' /etc/passwd

找出磁盘使用率高于80%的找出来(提示: df -p)

# df -P | awk '$NF~"/" && $3/$2>0.8 {print $1"磁盘使用超过80%了,已经使用"$5}'
/dev/sr0磁盘使用超过80%了,已经使用100%

# df -P| sed 1d |awk -F"[ %]+" '$5>80 {print $1"的磁盘使用率已达到"$5"%"}'

# df -P|awk '$0~"/" && $5~"[89][0-9]%" || $5=="100%"  {print $1"的磁盘使用率已达到"$5}'

找出18:30之前下班的打卡记录

# cat 1.txt
张三  2013-7-01 18:19:28
张三  2013-7-02 17:58:45
张三  2013-7-03 22:41:47
张三  2013-7-04 22:15:23
张三  2013-7-05 19:12:27
张三  2013-7-06 19:03:09
张三  2013-7-07 19:09:44
张三  2013-7-08 19:04:45
张三  2013-7-09 18:39:28
张三  2013-7-10 18:24:48
张三  2013-7-11 18:58:21
张三  2013-7-12 18:36:22
张三  2013-7-13 19:23:46
张三  2013-7-14 19:02:41
张三  2013-7-15 19:00:09
张三  2013-7-16 18:36:13
张三  2013-7-17 18:36:40
张三  2013-7-18 19:00:00
张三  2013-7-19 18:31:18
张三  2013-7-20 18:44:01
张三  2013-7-21 18:37:12
张三  2013-7-22 18:29:33

提示: 时间直接比较是可以的,但是如果你小时为个位数(比如9:00:00,你应该要写成09:00:00才可以直接比较)

# cat 1.txt | awk '$3<"18:30:00" {print $0}'
张三  2013-7-01 18:19:28
张三  2013-7-02 17:58:45
张三  2013-7-10 18:24:48
张三  2013-7-22 18:29:33

假设9点整上班,算出下面这几天这几个人分别迟到多少次,和扣多少钱(一次扣10块)

张三 2013-8-25 09:19:28
李四 2013-8-25 08:58:45
王五 2013-8-25 08:41:47
马六 2013-8-25 09:12:52
田七 2013-8-25 09:01:47
张三 2013-8-24 08:49:28
李四 2013-8-24 08:54:45
王五 2013-8-24 09:11:47
马六 2013-8-24 09:02:52
田七 2013-8-24 09:04:47
张三 2013-8-23 09:29:28
李四 2013-8-23 08:57:45
王五 2013-8-23 08:41:47
马六 2013-8-23 09:08:52
田七 2013-8-23 09:09:47
张三 2013-8-22 09:24:28
李四 2013-8-22 09:16:45
王五 2013-8-22 09:11:47
马六 2013-8-22 08:52:52
田七 2013-8-22 08:44:47
# cat 2.txt  |awk '$3>"09:00:00" {print $1}' |sort |uniq -c |awk 'BEGIN{print "姓名\t罚多少"}{print $2"\t"$1*10"元"}'
姓名    罚多少
张三    30元
李四    10元
王五    20元
田七    30元
马六    30元

sed分域操作

将()之间的字符串(一般为正则表达式)定义为组,并且将匹配这个表达式的保存到一个区域(一个正则表达式最多可以保存9个),它们使用\1到\9来表示。然后进行替换操作

注意: sed分域操作的格式就是替换修改.需要用到截取功能

示例:hello,world.sed变成world,sed.hello(注意: 1个逗号1个点号)

在这里插入图片描述

awk的做法

# echo "hello,world.sed" |awk -F[,.] '{print $2","$3"."$1}'
world,sed.hello

sed的做法:

方法1:
# echo "hello,world.sed" | sed 's/\(.*\),\(.*\)\.\(.*\)/\2,\3.\1/'
world,sed.hello
此方法\符太多了,建议使用-r扩展模式,就不用加\转义括号了

方法2:
# echo "hello,world.sed" | sed -r 's/(.*),(.*)\.(.*)/\2,\3.\1/'
world,sed.hello

方法3:
# echo "hello,world.sed" | sed -r 's/(.....)(.)(.....)(.)(...)/\3\2\5\4\1/'
world,sed.hello

方法4:
# echo "hello,world.sed" | sed -r 's/(.{5})(.)(.{5})(.)(.{3})/\3\2\5\4\1/'
world,sed.hello

示例: 以/etc/passwd文件前5行为例,进行如下处理

删除每行的第一个字符

# head -5 /etc/passwd |cut -c2-
# head -5 /etc/passwd |sed -r 's/(.)(.*)/\2/'
# head -5 /etc/passwd |sed -r 's/.//1'
# head -5 /etc/passwd |sed -r 's/^.//'

删除每行的第九个字符

# head -5 /etc/passwd |cut -c1-8,10-
# head -5 /etc/passwd |sed -r 's/(.{8})(.)(.*)/\1\3/'
# head -5 /etc/passwd |sed -r 's/.//9'

删除倒数第5个字符

# head -5 /etc/passwd |rev |cut -c1-4,6- |rev
# head -5 /etc/passwd |sed -r 's/(.*)(.)(....)/\1\3/'

把每行的第5个字符和第8个字符互换,并删除第10个字符

# head -5 /etc/passwd | sed -r 's/(....)(.)(..)(.)(.)(.)(.*)/\1\4\3\2\5\7/'

文本处理小结:

1, 截取操作:

  • 命令: cut,sed,awk,${ }
  • awk与sed截取比较: sed分域的括号里直接可以使用正则, awk在分隔符里可以使用正则

2, 行操作:

  • 命令: grep,sed,awk,head,tail
  • 通过行号: sed,awk
  • 通过正则匹配行: grep,sed,awk

3, 排序操作: sort

4, 去重操作: uniq

5, 统计操作: uniq -c ,wc, awk的BEGIN…END结构, 循环统计

6, 编辑操作: sed -i

shell总结

纯命令脚本

shell是按顺序从上往下执行命令

不用任何的判断与循环语法,就可以写shell脚本,但需要对命令熟悉。熟悉了命令就能写好最基本的脚本。

示例1

#!/bin/bash

mount /dev/cdrom /mnt &> /dev/null

rm /etc/yum.repos.d/* -rf

cat > /etc/yum.repos.d/local.repo <<EOF
[local]
name=local
baseurl=file:///mnt
enabled=1
gpgcheck=0
EOF
  • 问题1: mount /dev/cdrom /mnt &> /dev/null命令里我为什么要使用&>/dev/null将错误和正确的信息都扔掉?

答: 想知道答案,最好的方法就是把这条命令去复制到终端执行一下,并且尽量想想会不会有其它的情况,这样你的脚本才会更严谨。

在这里插入图片描述

  • 问题2: 上面的问题中扔掉了信息就真的万事大吉吗? 如果没有/mnt目录呢?

答: 可以先判断/mnt目录是否存在,或者直接使用mkdir -p /mnt目录创建一下都可以.

  • **问题3:**为什么我要使用rm /etc/yum.repos.d/* -rf命令?

答: 因为我只想用本地的yum源,并不想用默认的网络源,所以为了方便我才先删除。当然有些情况可能需要用到网络源,那就不要删除。所以写shell也要看实际情况与需求.

  • 问题4: 最后一句配置本地local.repo为什么要这么做?

答: 因为配置一段yum源,需要写多行。那么你可以选择如下几种方法:

  • 直接准备好一个配置好的文件,拷贝过去(此方法最简单,但如果执行脚本的机器是远程机器,还要先配置ftp或http这种服务,然后下载过去再拷贝 )
  • 使用echo 命令一行一行的追加,太麻烦
  • 使用我脚本里的方法,可以方便的实现多行追加(下图为此命令的详细解析)

在这里插入图片描述

EOF格式练习

  1. root用户执行脚本,实现切换到abc用户,使用abc用户的身份创建/tmp/123文件
准备工作:先创建abc这个用户
# useradd abc
#!/bin/bash

su - abc <<EOF
touch /tmp/456
exit
EOF
# su - abc -c "touch /tmp/789"
切换成abc的身份来执行-c参数后面的命令

2, 实现在mysql数据库里建一个库,库名假设叫test2.建库命令为create database test2;(在mysql登录后才能操作,bash下不能操作此命令)

准备工作:先确认安装rpm版mysql,并启动
# yum install mysql mysql-server -y
# /etc/init.d/mysqld restart

# mysql										# mysql登录
mysql> show databases;						# 这是查看有哪些库的命令,用来验证(需要EOF传入)
mysql> quit									# 退出mysql登录(需要EOF传入)
#!/bin/bash

mysql <<EOF
create database test2;
quit
EOF

示例2

#!/bin/bash
mkdir /backup/$(date +%Y)/$(date +%m)/  -p

mv /var/log/httpd/access_log /backup/$(date +%Y)/$(date +%m)/$(date +%F).access_log
touch /var/log/httpd/access_log
/etc/init.d/httpd reload  &> /dev/null

logger -t "日志轮转" "$(date +%F)成功"
  • 问题1: 为什么要写shell来做日志轮转?

答: 因为我们学的logrotate命令只能实现daily(每天),weekly(每周),monthly(每月),yearly(每年)等时间间隔的轮转。如果我要每2天轮转1次怎么办? 或者每半个月轮转1次怎么办? 所以可以自己写轮转脚本再配合crond时间任务来实现轮转时间的自由控制

而且除了logrotate外还有其它的日志轮转工具。但我们运维工程师为什么要那么累去学习各种工具呢? 所以我们可以自己写日志轮转脚本,用统一的方式来管理,而不用去学习过多的工具,工具是学不完的。

  • 问题2: 为什么要先mkdir /backup/$(date +%Y)/$(date +%m)/ -p创建目录?

答: 日志轮转的第1步就是将写了一定时间周期的日志给移到别的目录,我这里需要一个/backup/年/月/这样的目录来存放轮转的日志,所以要先确保有此目录。

  • 问题3: 为什么/etc/init.d/httpd reload &> /dev/null刷新一下?

答: 因为mv日志文件后,需要通知一下服务,才能让新的日志记录到touch的新日志文件里。通过我们的学习再次回顾下服务reload其实就是kill -HUP $(cat /var/run/httpd.pid).

补充: 每个服务的pid文件只是用来记录服务的PID而已,路径也各不相同,以后会学到一些服务可以自己指定pid文件的路径.

  • 问题4: 为什么最后做logger -t "日志轮转" "$(date +%F)成功"

答: 轮转成功后也需要做一个记录,或记录日志或发邮件通知,我这里选择的是日志记录。

思想小结

纯命令脚本一定要对命令和你要做的事情熟悉,主要注意以下几点:

  • 脚本出错,如果不会看错误的话,可以1条1条的命令在终端执行看看是否能执行成功
  • 尽量想想会不会有其它的情况,这样你的脚本才会更严谨(如目录是否存在,空间是否足够等,可能会用到判断)
  • 写shell也要看实际情况与需求

变量

变量是用来临时保存数据的,但什么时候定义变量? 我来总结以下几种情况

1, 多次需要被调用的值可以使用变量

下面的脚本里时间被多次调用,可以定义变量为了让脚本更简便

#!/bin/bash

year=$(date +%Y)						# 命令的结果作为值赋值给变量,需要执行符$()
month=$(date +%m)
date_time=$(date +%F)

mkdir /backup/$year/$month/   -p

mv /var/log/httpd/access_log /backup/$year/$month/$date_time.access_log
touch /var/log/httpd/access_log
/etc/init.d/httpd reload  &> /dev/null

logger -t "日志轮转" "$date_time成功"

易错点:

有同学只记住了多次被调用,但却没有记清楚是才能赋值,如一个数字,一串字符,一个命令的结果等

a=$(&> /dev/null)			
有同学觉得脚本里总要扔信息,就想做这样一个变量
但问题是:后面的&> /dev/null并不是一个能完整执行的命令

2, 用于保存值,让变化的值在某一个时间或时刻保存下来

如: 把随机数保存下来,然后可以对其猜数字

num=$[$RANDOM%100+1]

如: 在登录的时间点将时间保存到变量

# vim /home/abc/.bash_profile 
export logintime=$(date +%s)

易错点:

同一条命令如果在不同的时间执行或执行不同的次数如果会有不同的结果,你希望保留第1次的结果就用变量保存起来

echo $[$RANDOM%900+100]	 | passwd --stdin abc &> /dev/null
echo "abc:$[$RANDOM%900+100]" >> /root/.passwd

改正

password=$[$RANDOM%900+100]		

echo $password | passwd --stdin abc &> /dev/null
echo "abc:$password" >> /root/.passwd
把值保存到变量,否则如果产生2次随机数的话,2次的结果会不一样
  1. 需要交互的情况,一般都需要定义变量来保存用户输入的值

如: 写shell的作者不知道用户要查找的目录是什么,所以需要定义一个变量来保存用户输入的值

read -p "输入你要查找的目录:" dir

易错点:

要对密码算长度, 但这个变量为是cat它还是echo它呢?

read -s -p "输入你的密码:" password

length=$(cat $password |wc -L)
cat接文件,echo接字符串.所以这里不是cat,应该是echo.
如果记不住,前面我们说过把执行符号里的命令拿来执行一下就知道对与错了

易错点:

下面的写法完全没问题,但要想清楚你赋值的是软件包名不是命令名. 比如你要查找vim命令的软件包是否安装,就不应该输入vim,而应该输入vim-enhanced(因为vim的软件包名为vim-enhanced)

read -p "输入软件包名:" name

rpm -q $name &> /dev/null
read -p "输入你要使用的命令:" command

which $command &> /dev/null

判断

  • 为什么要判断?

答: 因为写程序有太多需要判断情况的分支选择场景,如

  • 我想查看文件,得先判断文件是否存在
  • 我想查找一目录里的文件,得先判断目录是否存在
  • 我想跟一个服务器通讯,得先判断网络是否能通,服务是否OK
  • 我想安装一个软件,得先判断这个软件是否已经安装
  • 我想往一个目录里放东西,得先判断目录是否存在,目录空间是否足够
  • 我设置一个密码,得先判断这个密码是否符号一定的复杂度
  • 我登录一个密码,得先判断这个密码是否正确
  • 等等太多太多的应用场景了
  • 怎么找判断点?

答:

  1. 寻找判断点主要看对判断事物的熟悉程度。

题目: 如何判断一个目录里的空间大于3G

(补充:1G=1024M,1M=1024KB,1KB=1024B,B就是字节,Byte,1Byte=8bit)

read -p "输入一个目录:" dir

#!/bin/bash

read -p "输入一个目录:" dir

if [ ! -d $dir ];then
        echo "$dir不是一个目录,退出"
        exit
fi

size=$(df $dir |tail -1 | awk '{print $4}')

if [ $size -ge 3000000 ];then
        echo "空间足够"
else
        echo "空间不够,退出"
fi
  1. 如果不会找,就从不同点开始找. 比如判断1个用户是否存在,就要从所学的知识中去找存在1个用户与不存在1个用户有什么不同点就可以了,方法一般也会有多种.
read -p "输入一个用户:" user

id $user &> /dev/null

if [ $? -eq 0 ];then
	echo "$user存在"
else
	echo "$user不存在"
fi
read -p "输入一个用户:" user

grep ^$user: /etc/passwd

if [ $? -eq 0 ];then
	echo "$user存在"
else
	echo "$user不存在"
fi
  • 怎么确定选用哪种分支?

答: 判断一共就是单分支,双分支,多分支三种.

单分支比较特殊, 满足条件就做相应动作,不满足则不管。而用双分支还是多分支主要看判断的事物有几种可能。

  • 如判断目录是否存在,只有存在和不存在两种可能,可用单分支或双分支
  • 如判断输入的性别,有男,女和输入错误3种可能,3种可能都有相应的动作的话则为多分支

循环

重复的事情或动作就循环

shell里三种循环结构:for,while,until. 除了until外,for与while循环都用得比较多

练习: 写一个倒计时脚本,要求显示离2020年1月1日的凌晨0点,还有多少天,多少时,多少分,多少秒


文本处理

正则表达式与grep,sed,awk等综合的应用还是会让各位同学有些混乱,这是一种正常现象,不用着急,通过一些习题的练习慢慢的去应用它。

再次回顾grep,sed,awk三个工具的应用比较:

命令工具说明
grep更适合单纯的查找(通过要查找的关键字)或匹配(通过正则)
sed更适合编辑文本(行删除,行打印,行增加,替换与修改等),sed分域操作也适合做截取
awk更适合格式化文本,对文本进行较复杂格式处理(截取,行查找, 循环统计等)

下面不再讲解语法,我们通过几个习题来找找感觉。

在做题时,请按着下面三步来做:

  • 从哪找(是找行,还是找列,还是找某一行的某一列,还是某几行的某几列);由此来选择是使用grep还是sed还是awk.如果实在不会选,则可以尝试3种都试试,很可能3种都可以,看看哪个好
  • 用什么找(知道行号就以行号来找,不知道行号只知道关键字就用正则匹配来找)
  • 正则怎么匹配(正则匹配是开头,还是结尾,还是几个字符等)

1.查找/etc/passwd里注释列(第5列)不为空的行


2,查找/etc/passwd里注释列(第5列)为空的行


3,修改/etc/passwd里root用户的注释为"super admin"


4,查找/etc/passwd文件里用户名里包含有a字母的用户个数


5, 查找swap分区的uuid


6,判断你的主机名是否为FQDN格式


课外作业(不强制)

有兴趣的可以将前面学习的搭建nfs,vsftpd,rsync,samba,httpd,lamp等过程.写成脚本

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七十一阿哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值