文件格式化处理

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.
...