文件格式化处理
1、sed工具
[root@li ~]# sed [-nefr] [动作]
选项与参数:
-n:使用安静模式。在一般的 sed 用法中,所有来自 STDIN 的数据一般都会被列出到屏幕上。如果加上 -n 参数后,只有经过处理的那一 行才会被列出来
-e:直接在指令列模式上进行 sed 的动作编辑
-f:直接将 sed 的动作写在一个文件内
-r:sed 的动作支持的是延伸的正则表达式
-i:直接修改读取文件内容,而不是屏幕输出
动作说明: '[n1[,n2]]function'(一定要有单引号)
n1,n2:不见得存在,一般代表 “选择进行动作的行数”
function:
a:新增,a 的后面可以接字符串,而这些字符串会在新的一行出现(目前的下一行)
c:取代,c 的后面可以接字符串,这些字符串可以取代 n1,n2 之间的行
d:删除
i:插入,i 后面可以接字符串,而这些字符串会在新的一行出现(目前的上一行)
p:打印,通常 p 会将参数 sed -n 一起出现
s:取代,可以直接进行取代的工作。通常这个 s 的动作可以搭配正则表达式
1.1、以行为单位的新增/删除功能
#将 /etc/passwd 的内容列出来并打印行号,同时,删除第 2~5 行
[root@li ~]# nl /etc/passwd | sed '2,5d'
1 root:x:0:0:root:/root:/bin/bash
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
...
#在第二行后(也就是加在第三行)加上 drink tea 的字样
[root@li ~]# nl /etc/passwd | sed '2a drink tea'
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
drink tea
...
#在第二行后加上两行字(要使用反斜杠)
[root@li ~]# nl /etc/passwd | sed '2a drink tea ...\
> drink beer?'
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
drink tea ...
drink beer?
...
1.2、以行为单位的取代与显示功能
#将第 2~5 行取代为 No 2-5 number
[root@li ~]# nl /etc/passwd | sed '2,5c No 2-5 number'
1 root:x:0:0:root:/root:/bin/bash
No 2-5 number
6 sync:x:5:0:sync:/sbin:/bin/sync
...
#仅列出第 5~7 行数据
[root@li ~]# nl /etc/passwd | sed -n '5,7p'
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
1.3、部分数据的搜寻并取代的功能
sed 's/要被取代的字符串/新的字符串/g'
#先用 ifconfig 查询 IP
[root@li ~]# ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.0.246 netmask 255.0.0.0 broadcast 10.255.255.255
inet6 fe80::91cd:d416:48f5:585d prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:5d:05:76 txqueuelen 1000 (Ethernet)
RX packets 4950 bytes 333302 (325.4 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8784 bytes 1012348 (988.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
#利用关键字配合 grep 截取出关键的一行数据
[root@li ~]# ifconfig ens33 | grep 'inet '
inet 10.0.0.246 netmask 255.0.0.0 broadcast 10.255.255.255
#将 IP 前面的部分删除
[root@li ~]# ifconfig ens33 | grep 'inet ' | sed 's/^.*inet //g'
10.0.0.246 netmask 255.0.0.0 broadcast 10.255.255.255
#将 IP 后面的部分删除
[root@li ~]# ifconfig ens33 | grep 'inet ' | sed 's/^.*inet //g' | sed 's/ *netmask.*$//g'
10.0.0.246
#先用 grep 将关键字 MAN 所在行取出来
[root@li ~]# cat /etc/man_db.conf | grep 'MAN'
# MANDATORY_MANPATH manpath_element
# MANPATH_MAP path_element manpath_element
# MANDB_MAP global_manpath [relative_catpath]
# every automatically generated MANPATH includes these fields
...
#删掉批注后的数据
[root@li ~]# cat /etc/man_db.conf | grep 'MAN' | sed 's/#.*$//g'
MANDATORY_MANPATH /usr/man
MANDATORY_MANPATH /usr/share/man
MANDATORY_MANPATH /usr/local/share/man
...
#删掉空白行
[root@li ~]# cat /etc/man_db.conf | grep 'MAN' | sed 's/#.*$//g' | sed '/^$/d'
MANDATORY_MANPATH /usr/man
MANDATORY_MANPATH /usr/share/man
MANDATORY_MANPATH /usr/local/share/man
1.4、直接修改文件内容
很危险的操作,与参数 -i 有关。
2、文件的格式化与相关操作
2.1、格式化打印:printf
[root@li ~]# printf '打印格式' 实际内容
选项与参数:
关于格式方面的几个特殊样式:
\a 警告声音输出
\b 退格键
\f 清除屏幕
\n 输出新的一行
\r 即 Enter 键
\t 水平的 tab 键
\v 垂直的 tab 键
\xNN NN 为两位数字,可以转换数字为字符
关于 C 语言内,常见的变数格式:
%ns 那个 n 是数字,s 代表 string,即多少字符
%ni 那个 n 是数字,i 代表 int,即多少整数字数
%N.nf 小数
2.2、awk:好用的数据处理工具
[root@li ~]# awk '条件类型1{动作1} 条件类型2{动作2} ...' 文件名
[root@li ~]# last -n 5
root pts/0 10.0.0.100 Fri Aug 7 08:26 still logged in
reboot system boot 3.10.0-1127.el7. Fri Aug 7 08:24 - 09:04 (00:39)
root pts/0 li-911m.lan Thu Aug 6 15:40 - crash (16:44)
reboot system boot 3.10.0-1127.el7. Thu Aug 6 15:40 - 09:04 (17:23)
root pts/0 li-911m.lan Thu Aug 6 08:56 - crash (06:43)
#取出账号与账号的 IP
[root@li ~]# last -n 5 | awk '{print $1 "\t" $3}'
root 10.0.0.100
reboot boot
root li-911m.lan
reboot boot
root li-911m.lan
整个 awk 的处理流程:
- 读入第一行,并将第一行的资料填入 $0,$1,$2,…当中;
- 依据 “条件类型” 的限制,判断是否需要进行后面的 “动作”;
- 做完所有的动作与条件类型;
- 若还有后续的 “行” 的数据,则重复前面的 1~3 步骤,直到所有的数据全部读完为止。
awk 是 “以行为一次处理的单位”,而 “以字段为最小的处理单位”。那 awk 怎么知道这个数据到底有几行?这就需要 awk 的内建变量的帮忙:
变量名称 | 说明 |
---|---|
NF | 每一行($0)拥有的字段总数 |
NR | 目前 awk 所处理的是 “第几行” 的意思 |
FS | 目前的分隔字符,默认是空格 |
我们继续以 last -n 5 举例,如果我们想要:
- 列出每一行的账号(也就是 $1);
- 列出目前处理的行数(就是 awk 内的 NR 变量);
- 并且说明,该行有多少字段(就是 awk 内的 NF 变量)。
[root@li ~]# last -n 5 | awk '{print $1 "\t lines:" NR "\t columns: " NF}'
root lines:1 columns: 10
reboot lines:2 columns: 11
root lines:3 columns: 10
reboot lines:4 columns: 11
root lines:5 columns: 10
awk 的逻辑运算符:
运算单元 | 说明 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
#第三栏小于 10 的数据,仅列出账号与第三栏
[root@li ~]# cat /etc/passwd | awk '{FS=":"} $3 < 10 {print $1 "\t " $3}'
root:x:0:0:root:/root:/bin/bash
bin 1
daemon 2
...
为什么第一行没有正确显示呢?因为我们在读入第一行的时候,那些变量 $1,$2,…默认还是以空格为分隔符的,所以定义的 FS=“:” 在第二行才开始生效。解决方法:使用 BEGIN 关键字:
[root@li ~]# cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t " $3}'
root 0
bin 1
daemon 2
2.3、文件比对工具
2.3.1、diff
diff 就是比对两个文件的差异,并且是以行为单位来比对的!一般用在 ASCII 纯文本文件的比对上。由于是以行为单位进行比对的,因此 diff 通常是用在同一文件(或软件)的新旧版本差异上。
[root@li ~]# diff [-bBi] from-file to-file
选项与参数:
from-file:原始比对文件
to-file:目的比对文件
from-file 或 to-file 可以用 - 取代
-b:忽略一行当中,仅有多个空白的差异
-B:忽略空白行的差异
-i:忽略大小写的差异
[root@li ~]# mkdir -p /tmp/testpw
[root@li ~]# cd /tmp/testpw/
[root@li testpw]# cp /etc/passwd passwd.old
[root@li testpw]# cat /etc/passwd | sed -e '4d' -e '6c no six line' > passwd.new
[root@li testpw]# diff passwd.old passwd.new
4d3 #左边第四行被删除了,基准是右边的第三行
< adm:x:3:4:adm:/var/adm:/sbin/nologin #被删除的那一行
6c5 #左边文件第六行被取代了
< sync:x:5:0:sync:/sbin:/bin/sync #被取代的内容
---
> no six line #右边文件的第五行
[root@li testpw]# diff /etc/rc0.d/ /etc/rc5.d/
只在 /etc/rc0.d/ 存在:K90network
只在 /etc/rc5.d/ 存在:S10network
2.3.2、cmp
cmp 主要是以字节来比较。
[root@li ~]# cmp [-l] 文件1 文件2
选项与参数:
-l:将所有不同点的字节列出来。因为 cmp 默认仅列出第一个不同点
[root@li testpw]# cmp passwd.old passwd.new
passwd.old passwd.new 不同:第 106 字节,第 4 行 #默认仅列出第一个不同点
[root@li testpw]# cmp -l passwd.old passwd.new
106 141 154
107 144 160
108 155 72
...
#内容太多
2.3.3、patch
patch 与 diff 之间密不可分。diff 用来比较两个文件之间的差异,那如何升级呢?就是 “先比较新旧版本的差异,并将差异文档做成补丁,再由补丁更新旧文件” 即可。
[root@li testpw]# diff -Naur passwd.old passwd.new > passwd.patch
[root@li testpw]# cat passwd.patch
--- passwd.old 2020-08-07 09:29:25.309779367 +0800
+++ passwd.new 2020-08-07 09:30:05.133777094 +0800
@@ -1,9 +1,8 @@
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
-adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
-sync:x:5:0:sync:/sbin:/bin/sync
+no six line
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
[root@li testpw]# patch -pN < patch_file #更新
[root@li testpw]# patch -R -pN < patch_file #还原
#更新文件
[root@li testpw]# patch -p0 < passwd.patch
patching file passwd.old
[root@li testpw]# ll passwd*
-rw-r--r--. 1 root root 778 8月 7 09:30 passwd.new
-rw-r--r--. 1 root root 778 8月 7 09:43 passwd.old #文件完全一样了
#还原文件
[root@li testpw]# patch -R -p0 < passwd.patch
patching file passwd.old
[root@li testpw]# ll passwd*
-rw-r--r--. 1 root root 778 8月 7 09:30 passwd.new
-rw-r--r--. 1 root root 835 8月 7 09:44 passwd.old #不一样了
2.4、文件打印准备:pr
[root@li testpw]# pr /etc/man_db.conf
2018-10-31 04:26 /etc/man_db.conf 第 1 页
#
#
# This file is used by the man-db package to configure the man and cat paths.
...