Linux 文件内容处理

提取数据列

可以使用 cut 命令按行提取数据并删除所需部分,命令格式如下:

cut -d '分隔字符' -f 编号

cut -c 字符范围

可用参数如下:

参数 说明
-d 后面接分隔字符,与 -f 一起使用。
-f 选取被切割后的第几段数据。
-c 以字符为单位取出固定字符区间。

例如以 : 为分隔符,取出 PATH 变量第 3 和第 5 段:

[root@101c7 abc]$ echo $PATH | cut -d ":" -f 3,5
/usr/sbin:/root/bin

export 输出信息取得第 12 个字符以后的所有字符串:

[root@101c7 abc]$ export|head -4;echo "-----";export | cut -c 12- | head -4
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="101c7"
-----
HISTCONTROL="ignoredups"
HISTSIZE="1000"
HOME="/root"
HOSTNAME="101c7"

如果要选取的值是 12 到 20 个字符,那么使用 cut -c 12-20

抓取数据

使用 grep 则是分析一行信息,有需要的信息则将整行取出.简单语法如下:

grep [-acinv] '查找字符串' 文件名

grep -[AB] '查找字符串' 文件名

可用参数如下:

参数 说明
-a 将二进制文件以 text 文件的方式查找数据
-c 计算找到 ‘查找字符串’ 的次数
-i 忽略大小写
-n 输出行号
-v 反向选择
-E 使用扩展正则表达式,默认使用基础正则表达式
-P 使用 Perl 正则表达式
-F 关闭正则表达式模式,将所有的字母都看作单词
-A/Bn 列出找到结果的后/前 n 行

例如取出 /etc/man_db.conf 中含有 MANPATH 的行:

[root@101c7 ~]$ grep 'MANPATH' /etc/man_db.conf 
# MANDATORY_MANPATH                     manpath_element
# MANPATH_MAP           path_element    manpath_element
# every automatically generated MANPATH includes these fields

忽略大小写,使用扩展正则表达式搜索打印出以 pw 开头的行,并显示行号:

[root@101c7 ~]$ cat anaconda-ks.cfg | grep -E -n -i '^pw.*'
45:pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
46:pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
47:pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty

向搜索以 # 开头以 s 结尾的行:

[root@101c7 ~]$ grep -E -i -vn '^#.*s$' anaconda-ks.cfg 
1:#version=DEVEL
2:# System authorization information
3:auth --enableshadow --passalgo=sha512

排序命令

使用 sort 命令能对文件内容进行排序。该命令的主要参数包括:

参数 说明
-f 忽略大小写差异;
-b 忽略最前面的空格;
-M 以月份的名字来排序;
-n 使用纯数字进行排序;
-r 反向排序;
-u 去除重复行;
-t 分隔符,默认是 TAB;
-k 以哪个分割区间来进行排序。

例如,将 passwd 文件内容按第一个字符排序:

[root@101c7 ~]$ cat /etc/passwd | sort | head -4
adm:x:3:4:adm:/var/adm:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

: 为分隔符,按第 3 列(数字)排序。也可以使用 -k 3,4 参数来先按第三列排序,再按第四列排序:

[root@101c7 ~]$ cat /etc/passwd | sort -t ':' -k 3 -n | head -5
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

如果要统计重复数据,可以使用 uniq 命令。需要注意的是,uniq 的输入必须是有序的。例如,使用 -c 参数统计用户登录次数,再以从少到多排序:

[root@101c7 ~]$ last | cut -d ' ' -f 1 | sort | uniq -c | sort -t ' ' -k 1 -n
      1 
      1 wtmp
      3 reboot
     13 root

统计文件信息

使用 wc 命令来统计文件信息,包括行数、单词数和文件大小。该命令可用的选项包括:

参数 说明
-l 查询文件内容行数;
-w 查询文件单词数;
-c 查询文件大小(单位 B)。

默认参数为 -lwc。例如,查询文件 anaconda-ks.cfg 的行数、单词数和大小:

[root@101c7 ~]$ wc anaconda-ks.cfg 
  48  122 1260 anaconda-ks.cfg

字符转换命令

可以使用 tr 命令删除一段信息中的文字,或进行文字信息的替换。该命令可用的参数包括:

参数 说明
-d 删除信息中的字符串;
-s 替换掉重复的字符。

例如,将 last 命令输出的信息中所有小写字母转换为大写:

[root@101c7 ~]$ last | tr '[a-z]' '[A-Z]'
ROOT     TTY1                          SAT SEP 11 09:35   STILL LOGGED IN   
ROOT     PTS/1        192.168.2.101    SAT SEP 11 08:58   STILL LOGGED IN 

删除所有 passwd 文件中的 DOS 断行符 \r 并另存为 px.txt

[root@101c7 ~]$ cat /etc/passwd | tr -d '\r' > px.txt

转换 Tab 为空格

在 Linux 中,默认每隔 8 个字符有一个制表位(Tab Stop)。可以使用 col 命令将文件中的 Tab 转换成对等的空格。例如:

[root@101c7 ~]$ cat -A /etc/man_db.conf | grep "#M"
#MANDATORY_MANPATH ^I^I^I/usr/src/pvm3/man$
#MINCATWIDTH^I80$
#MAXCATWIDTH^I80$
[root@101c7 ~]$ cat /etc/man_db.conf | col -x | cat -A | grep "#M"
#MANDATORY_MANPATH                      /usr/src/pvm3/man$
#MINCATWIDTH    80$
#MAXCATWIDTH    80$

以上命令将制表位转换成了等效的空格,从而消除了 ^I 的干扰。

另外,还可以使用 expand 命令将 Tab 转换成空格。例如,将 /etc/man_db.conf 文件中的制表位设置为 1,并将 Tab 转换成空格:

[root@101c7 ~]$ grep "#M" /etc/man_db.conf| expand -t 1 
#MANDATORY_MANPATH    /usr/src/pvm3/man
#MINCATWIDTH 80
#MAXCATWIDTH 80

如果需要将行开头的空格转换成 Tab,可以使用 unexpand 命令。该命令不会操作行中间的空格,因为大多数时候,只需要使用 unexpand 程序进行缩进。您还可以使用 -a 选项将所有空格替换为 Tab。

转换断行符

在 Windows 中,文本文件的换行符是 CRLF\r\n),而在 Linux 下则使用 LF 这个换行符。如果在 Linux 环境下执行 Windows 格式的脚本,可能会出现无法执行的错误。

可以通过 Notepad++ 的扩展搜索功能,将 \r 全部替换掉。也可以使用 dos2unixunix2dos 命令来转换文本文件中的换行符:

[root@101c7 ~]$ dos2unix -k root.txt 
dos2unix: converting file root.txt to Unix format ..

格式化行

如果需要将长行分隔成短行,可以使用 fold 命令。例如,将 anaconda-ks.cfg 文件中的行宽设置为 10 并且不分离单词:

[root@server3 ~]$ fold -s -w 10 anaconda-ks.cfg 
#version=D
EVEL
# System 
authorizat

格式化段落

如果需要将段落中的各行连接在一起,使段落尽可能短小和紧凑,可以使用 fmt 命令。该命令假定段落由空行分隔,因此一个段落就是一个或多个连续的文本行,不包含空格。

例如,将文本文件 anaconda-ks.cfg 按照行宽 50 个字符重新格式化段落,并合并连续的空格:

[root@server3 ~]$ fmt -u -w 50 anaconda-ks.cfg 
#version=DEVEL # System authorization information
auth --enableshadow --passalgo=sha512 # Use

编码转换

可以使用 locale 命令查看与系统编码相关的设置。例如:

[root@101c7 ~]$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"

如果需要修改系统编码以处理乱码问题,只需要重新定义对应的变量并导出即可。例如:

[root@101c7 ~]$ export LANG=en_US.UTF-8 ; locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"

如果需要转换文件编码,可以使用 iconv 命令将文件编码转换为另一种格式。例如,将 1.txt 文件的原始编码 big5 转换为 UTF-8

[root@101c7 4]$ iconv -f big5 -t utf8 1.txt -o 1utf8.txt
[root@101c7 4]$ file 1utf8.txt 
1utf8.txt: ASCII text

另外,还可以使用 iconv 命令将繁体中文转换为简体中文。例如,将 1.txt 中的繁体中文转换为简体中文 GB2312 编码格式,并将结果保存到 1.gb.txt 文件中:

[root@101c7 4]$ iconv -f utf8 -t big5 1.txt | iconv -f big5 -t gb2312 | iconv -f gb2312 -t utf8 -o 1.gb.txt 

文件合并

可以使用 join 命令将两个文件中有相同数据的那一行加在一起。例如,将 passwdshadow 相关的数据整合成一列:

[root@101c7 ~]$ join -t ':' /etc/passwd /etc/shadow
bin:x:1:1:bin:/bin:/sbin/nologin:*:17632:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin:*:17632:0:99999:7:::
adm:x:3:4:adm:/var/adm:/sbin/nologin:*:17632:0:99999:7:::

还可以使用 paste 命令将两个文件按行拼接在一起,用 Tab 分隔两个文件的行。例如:

[root@101c7 ~]$ paste /etc/passwd /etc/shadow | head -3
bin:x:1:1:bin:/bin:/sbin/nologin        bin:*:17632:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:*:17632:0:99999:7:::

以上命令将 passwdshadow 两个文件的每一行按顺序拼接在一起,并用 Tab 分隔两个文件的行。

文件切割

可以使用 split 命令将大文件分割成小文件,参数如下:

参数 说明
-b 以指定大小进行切割
-l 以行数来进行切割
-d 以两位的数字而不是字母作为后缀
-a 指定在后缀中使用的数字或字符的数量
PREFIX 代表前导符,可作为切割文件的前导文字

例如将 test.iso 切割成每个文件大小为 5M:

[root@101c7 sdb4m]$ split -b 5M test.iso file.sp | ll | grep 'file'
-rw-r--r--. 1 root root  5242880 Sep 11 12:35 file.spaa
-rw-r--r--. 1 root root  5242880 Sep 11 12:35 file.spab
-rw-r--r--. 1 root root  5242880 Sep 11 12:35 file.spac
-rw-r--r--. 1 root root  5242880 Sep 11 12:35 file.spad

可以使用数据流重定向来合并切割后的文件:

[root@101c7 sdb4m]$ cat file1.spa* >> test1.iso;ll | grep 'test'
-rw-r--r--. 1 root root 20971520 Sep 11 12:38 test1.iso
-rw-r--r--. 1 root root 20971520 Sep 11 12:33 test.iso

如果不带参数,split 命令会以 1000 行作为切割条件。可以使用 -l 选项指定每 5 行记录成一个文件:

[root@101c7 sdb4m]$ ls -la | split -l 10 - lsla;wc -l lsla*
10 lslaaa
  3 lslaab
13 total

一般来说,如果需要标准输入输出且没有文件,只有 - 时,- 就被当成标准输入或标准输出。

文件比较

在 Linux 系统中,比较文件可以使用 diff 命令。diff 命令会输出将左边的文件转换为右边的文件所需的修改内容。

命令格式为:diff [-bBi] 要比较的文件 基准文件

参数:

参数 说明
-b 忽略一行中仅有多个空白的区别,例如"a man"与"a man"视为等同
-B 忽略空白行的区别
-i 忽略大小写

例如,比较 c.cfgcnew.txt 两个文件的区别:

[root@101c7 sdb4m]$ cat c.cfg 
abcaca
/root/etc/
1.4
1a4
[root@101c7 sdb4m]$ cat cnew.cfg 
abcaca
/root/etc
1.4
[root@101c7 sdb4m]$ diff c.cfg cnew.cfg 
2c2
< /root/etc/
---
> /root/etc
4d3
< 1a4

比较结果说明如下:

说明
1 2c2 左边和右边比第 2 行不一样(c=change 改变,d=delete 删除,a=append 追加)
2 < /root/etc/ 显示左边文件不同的第 2 行内容
3 左右两个文件区分显示的分隔符
4 > /root/etc 显示右边文件不同的第 2 行内容
5 4d3 显示左边第 4 行和右边第 3 行比不同(d 代表删除)
6 < 1a4 显示左边文件第 4 行内容,而右边第 4 行不存在(被删除)

可以直接将文件比较的区别制作成补丁文件(也叫差分),供要比较的文件使用升级。优点是反升级和升级一样方便。制作补丁参数为 -Naur:

[root@101c7 sdb4m]$ diff -Naur c.cfg cnew.cfg > c.patch;cat c.patch
--- c.cfg       2021-09-12 05:52:16.000000000 -0400
+++ cnew.cfg    2021-09-12 13:25:05.000000000 -0400
@@ -1,4 +1,3 @@
abcaca
-/root/etc/
+/root/etc
1.4
-1a4

提供给左侧文件使用的补丁内容:

说明
1 — c.cfg 左边文件信息,— 表示旧文件
2 +++ cnew.cfg 右边文件信息,+++ 表示新文件
3 @@ -1,4 +1,3 @@ 修改数据的界定范围,旧文件在 1-4 行,新文件在 1-3 行
4 abcaca 第一行内容,内容前面没有符号表示不需要变动
5 -/root/etc/ 左侧文件第二行内容。减号 - 表示删除此行
6 +/root/etc 右侧文件第二行内容。加号 + 表示增加此行
7 1.4 第三行内容
8 -1a4 左侧文件第四行内容,- 表示要删除此行

打文件补丁使用 patch 命令,参数为 -pN,其中 N 为数字,表示取消几层目录的意思。

例如打补丁 c.patch:

[root@101c7 sdb4m]$ patch -p0 <c.patch ; cat c.cfg 
patching file c.cfg
abcaca
/root/etc
1.4

恢复旧文件内容使用参数 -R -pN

[root@101c7 sdb4m]$ patch -R -p0 <c.patch ; cat c.cfg 
patching file c.cfg
abcaca
/root/etc/
1.4
1a4

也可以用 diff 来比较目录下的区别。例如比较不同挂载点下的 lost+found 目录:

[root@101c7 sdb4m]$ diff /root/sdb4m/lost+found/ /ext333/lost+found/
Only in /root/sdb4m/lost+found/: 1
Only in /root/sdb4m/lost+found/: 2

相对于 diff 以行为单位比较,cmp 则是以字节为单位去比较,因此可以比较任何类型的文件。例如还是比较 c.cfg 与 cnew.cfg:

[root@101c7 sdb4m]$ cmp c.cfg cnew.cfg 
c.cfg cnew.cfg differ: byte 17, line 2

其结果显示两个文件不同点在第 2 行,第 17 个字节处。