vim的三模式转换、文件速查命令、文本内容多种排序去重

一、vi 和 vim:Linux 里的经典文本编辑器

vivim 都是 Linux/Unix 系统中非常经典的文本编辑器。vi 是原始版本,很多系统默认自带;vim 是从 vi 发展出来的增强版,全称是 Vi IMproved,它兼容 vi 的操作,同时增加了语法高亮、多级撤销、分屏、搜索高亮等功能。

简单理解:vi 是基础款,vim 是加强款。实际运维中更常用 vim,但掌握 vi 的模式和按键逻辑也很重要,因为很多最小化系统里默认可能只有 vi

vi 和 vim 的区别

编辑器 说明
vi 原始版本,Unix 系统标配的文本编辑器
vim 增强版 vi,兼容 vi 操作,功能更丰富

常见功能对比如下:

功能 vi vim
语法高亮 不支持 支持,可使用 syntax on
多级撤销 通常较弱 支持多级撤销,常用 u / Ctrl + R
多窗口 / 分屏 不支持 支持 :split / :vsplit
图形界面 支持 gvim
脚本扩展 不支持 支持 VimScript / Lua
搜索高亮 较弱 支持 :set hlsearch
命令行历史 有限 更完整
鼠标操作 不支持 可使用 :set mouse=a

二、vim 的安装和常用工具包

vi 一般系统自带,但 vim 在最小化安装环境里可能没有。如果执行 vim 提示命令不存在,可以先安装。

在 CentOS 7 场景下,可以先备份 yum 源配置文件:

bash 复制代码
cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak

下载 yum 源配置文件:

bash 复制代码
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

安装 vim 和一些常用 Linux 工具:

bash 复制代码
yum install -y tree vim wget bash-completion bash-completion-extras lrzsz net-tools sysstat iotop iftop htop unzip nc nmap telnet bc psmisc httpd-tools bind-utils nethogs expect

这里不只是安装 vim,还顺带安装了一些运维常用工具。比如 tree 看目录树,wget 下载文件,net-tools 提供 ifconfig 等命令,htop 更直观看进程,lrzsz 方便上传下载文件。

三、vim 的三种模式

vim 最容易卡住的地方就是模式。打开文件后不是马上就能输入文字,因为默认进入的是普通模式。只有切换到插入模式,输入的内容才会真正写进文件。

模式 进入方式 作用 退出方式
普通模式 启动默认进入,或按 Esc 移动光标、复制、删除、粘贴等 不需要单独退出
插入模式 iao 编辑文本内容 Esc 回到普通模式
底行模式 普通模式下输入 :/ 保存、退出、搜索、替换等 执行命令后一般回到普通模式

可以把它理解成三种工作状态:

  • 普通模式:像"操作遥控器",移动、删除、复制、粘贴。
  • 插入模式:像"真正打字",输入文件内容。
  • 底行模式:像"下命令",保存、退出、搜索、替换。

四、vim 普通模式常用操作

使用下面命令打开文件后,默认就是普通模式:

bash 复制代码
vim test.txt

普通模式主要负责光标移动、删除、复制、粘贴、撤销等操作。

光标移动操作

操作 说明
k 向上移动光标
j 向下移动光标
h 向左移动光标
l 向右移动光标
0^ 移动光标到行首
$ 移动光标到行尾
gg 移动光标到文件首行
G 移动光标到文件尾行
数字 + G 移动光标到指定行

示例:

bash 复制代码
gg      # 跳到第一行
G       # 跳到最后一行
20G     # 跳到第 20 行
0       # 跳到当前行行首
$       # 跳到当前行行尾

这些操作在修改配置文件时很实用。比如报错提示配置文件第 48 行有问题,就可以在普通模式下输入 48G,快速跳到目标行。

删除、复制和粘贴操作

操作 说明
dd 删除光标所在行
数字 + dd 从当前行开始删除多行
u 撤销上一步
x 删除光标所在字符,类似 Delete
X 删除光标前一个字符,类似 Backspace
yy 复制光标所在行
p 粘贴
dw 删除一个单词
yw 复制一个单词

示例:

bash 复制代码
dd      # 删除当前行
5dd     # 删除当前行开始的 5 行
yy      # 复制当前行
3yy     # 复制当前行开始的 3 行
p       # 粘贴到光标后面
u       # 撤销

举个常见场景:修改配置文件前,可以先复制一行再改。

bash 复制代码
yy
p

这样可以把原配置复制一份,保留参考内容,再修改新复制出来的那一行。

五、vim 插入模式常用操作

普通模式下不能直接输入正文。要进入插入模式,需要按下面这些键:

操作 说明
i 在当前光标前进入插入模式
a 在当前光标后进入插入模式
o 在当前光标下一行新开一行进入插入模式
I 在当前行行首进入插入模式
A 在当前行行尾进入插入模式
O 在当前光标上一行新开一行进入插入模式

常见选择方式:

  • 想在当前位置前面补内容,用 i
  • 想在当前位置后面继续写,用 a
  • 想在下一行新增配置,用 o
  • 想在当前行末尾追加内容,用 A

编辑完成后,一定要按 Esc 回到普通模式,再执行保存或退出。

六、vim 底行模式常用操作

普通模式下输入 :/,就会进入底行模式。它主要用于保存、退出、搜索、显示行号、替换等操作。

操作 说明
:w 保存
:q 退出
:! 强制操作
:wq 保存并退出
:q! 强制退出,不保存
/关键字 根据关键字向下搜索
n 搜索下一个匹配项
N 搜索上一个匹配项
:noh 取消搜索高亮
:set nu 显示行号
:set nonu 取消显示行号
:%s/old/new/g 将当前文件中所有匹配的 old 替换为 new

保存退出的区别

命令 说明
:q 文件未修改时退出
:wq 保存并退出
:q! 不保存,强制退出

如果文件已经修改,再执行 :q 会报错:

bash 复制代码
E37: 已修改但尚未保存 (可用 ! 强制执行)

原因很简单:vim 发现你改了内容,但没有保存,所以不让你直接退出。此时要么 :wq 保存退出,要么 :q! 放弃修改。

搜索和替换示例

搜索 root

bash 复制代码
/root

搜索后,按 n 跳到下一个匹配项,按 N 跳到上一个匹配项。

全文替换:

bash 复制代码
:%s/old/new/g

这条命令表示把当前文件中所有 old 替换成 new。比如把配置里的 localhost 改成 127.0.0.1

bash 复制代码
:%s/localhost/127.0.0.1/g

七、vim 常见问题:异常退出后的 .swp 文件

如果编辑文件时没有正常退出,比如使用 Ctrl + Z 挂起了编辑进程,再次打开同一个文件时,可能会提示存在交换文件,也就是 .swp 文件。

.swp 文件是 vim 用来保存编辑现场的临时文件。它有好处,比如异常断开后可以恢复内容;但如果确认不需要恢复,或者确认没有其他人正在编辑这个文件,就可以删除。

处理步骤:

bash 复制代码
ls -a
rm -f .test.txt.swp
vim test.txt

注意不要一看到 .swp 就马上删。如果是在生产环境编辑配置文件,先确认没有其他终端正在编辑同一个文件,避免覆盖别人的修改。

八、文件查看命令:head、tail、less、more

Linux 文件经常比较长,尤其是日志文件。不同场景下应该选不同命令:看开头用 head,看结尾用 tail,看大文件用 less,按页简单浏览可以用 more

九、head:查看文件开头内容

head 用于查看文件开头部分内容,默认显示前 10 行。

选项 说明
-n 数字 指定查看文件开头多少行

查看 /etc/passwd 前 10 行:

bash 复制代码
[root@atguigu ~]# head /etc/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
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
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
operator:x:11:0:operator:/root:/sbin/nologin

查看前 5 行有三种常见写法:

bash 复制代码
[root@atguigu ~]# head -5 /etc/passwd
[root@atguigu ~]# head -n 5 /etc/passwd
[root@atguigu ~]# head -n5 /etc/passwd

输出效果相同:

bash 复制代码
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

使用场景:快速确认文件格式、查看 CSV 表头、查看日志开头时间、检查配置文件前几行是否正常。

十、tail:查看文件结尾内容

tail 用于查看文件结尾部分内容,默认显示最后 10 行。

选项 说明
-n 数字 指定查看文件结尾多少行
-f 跟随查看,实时显示文件尾部新增内容

查看 /etc/passwd 最后 10 行:

bash 复制代码
[root@atguigu ~]# tail /etc/passwd
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin

查看最后 5 行:

bash 复制代码
[root@atguigu ~]# tail -5 /etc/passwd
[root@atguigu ~]# tail -n 5 /etc/passwd
[root@atguigu ~]# tail -n5 /etc/passwd

输出示例:

bash 复制代码
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin

实时查看文件尾部更新:

bash 复制代码
[root@atguigu ~]# tail -f /etc/passwd

tail -f 很适合看日志,因为日志通常都是追加到文件尾部。比如排查 Nginx 访问日志:

bash 复制代码
tail -f /var/log/nginx/access.log

再比如看系统日志:

bash 复制代码
tail -f /var/log/messages

执行后命令不会自动退出,会一直等文件追加新内容。要退出可以按 Ctrl + C

十一、less:支持翻页和搜索的文件浏览器

less 可以浏览文件内容,支持向上翻页、向下翻页、搜索、跳转到指定行。查看大文件时,less 比直接 cat 更舒服。

操作或选项 说明
less -N 文件 查看文件并显示行号
q 退出
PageDown / 空格 / f 向下翻一页
PageUp / b 向上翻一页
/字串 向下搜索指定字符串
?字串 向上搜索指定字符串
n 跳到下一个匹配项
N 跳到上一个匹配项
g 跳转到第一行
G 跳转到最后一行
数字g 跳转到指定行

查看文件并显示行号:

bash 复制代码
less -N /etc/passwd

进入 less 后搜索 sshd

bash 复制代码
/sshd

n 继续找下一个,按 q 退出。

使用场景:日志文件很大、不想一次性刷屏、需要反复上下翻页搜索时,用 less 更合适。

十二、more:按页查看文件内容

moreless 类似,也可以按页查看文件内容。它比 cat 更适合看长文件,但灵活性一般不如 less

操作 说明
空格 向下翻一页
Enter 向下翻一行
q 退出
b 返回上一页
= 输出当前行号
:f 输出文件名和当前行号

示例:

bash 复制代码
more /etc/passwd

简单对比:

命令 适合场景
cat 文件很短,一次性打印
more 简单分页查看
less 大文件浏览、搜索、上下翻页
head 只看开头
tail 只看结尾或实时日志

十三、wc:统计行数、单词数、字节数

wcword count 的缩写,可以统计指定文件的行数、单词数和字节数。

选项 说明
-l 只统计行数
-w 只统计单词数
-c 只统计字节数

准备测试文件:

bash 复制代码
[root@atguigu ~]# cat a.txt
11111111111
22222222222
33333333333

默认统计:

bash 复制代码
[root@atguigu ~]# wc a.txt
 3  3 36 a.txt

这三个数字分别表示:行数、单词数、字节数。

只统计行数:

bash 复制代码
[root@atguigu ~]# wc -l a.txt
3 a.txt

只统计单词数:

bash 复制代码
[root@atguigu ~]# wc -w a.txt
3 a.txt

只统计字节数:

bash 复制代码
[root@atguigu ~]# wc -c a.txt
36 a.txt

实际排障里,wc -l 用得很多。比如统计日志里有多少行报错:

bash 复制代码
grep "ERROR" app.log | wc -l

统计当前目录下有多少个 .conf 文件:

bash 复制代码
ls *.conf | wc -l

十四、which:查询命令位置

which 用来查询命令所在位置,它会在环境变量 $PATH 设置的目录里查找符合条件的可执行文件。

查询多个命令的位置:

bash 复制代码
[root@atguigu ~]# which cd head tail wc shutdown
/usr/bin/cd
/usr/bin/head
/usr/bin/tail
/usr/bin/wc
/usr/sbin/shutdown

如果查询不存在的命令,会提示找不到:

bash 复制代码
[root@atguigu ~]# which aaa
/usr/bin/which: no aaa in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)

使用场景:当一台机器上可能安装了多个同名命令,或者脚本执行结果和预期不一样时,可以先用 which 看看当前执行的是哪个路径下的命令。

十五、whereis:查询命令及相关文件位置

whereis 可以查询命令以及相关文件位置,通常用于查找二进制文件、源代码文件和 man 手册页。

选项 说明
-b 只查找二进制文件
-m 只查找说明文件,也就是 man 手册

查询 cd 命令相关位置:

bash 复制代码
[root@atguigu ~]# whereis cd
cd: /usr/bin/cd /usr/share/man/man1/cd.1.gz /usr/share/man/mann/cd.n.gz

只查询二进制文件位置:

bash 复制代码
[root@atguigu ~]# whereis -b cd
cd: /usr/bin/cd

只查询 man 手册页位置:

bash 复制代码
[root@atguigu ~]# whereis -m cd
cd: /usr/share/man/man1/cd.1.gz /usr/share/man/mann/cd.n.gz

whichwhereis 的区别可以这样记:

命令 重点
which 查当前 shell 实际会执行哪个命令
whereis 查命令相关文件,比如二进制文件、手册文件

十六、diff:逐行比较文件差异

diff 用来比较两个文本文件的差异,它会逐行比较内容,告诉我们哪里新增、删除、修改。

常见标记:

标记 英文 含义
a add 文件 1 的某行后面需要添加内容,才能变成文件 2
d delete 文件 1 的某些内容需要删除,才能变成文件 2
c change 文件 1 的某些内容需要修改,才能变成文件 2
< - 第一个文件中的内容
> - 第二个文件中的内容

几个标记例子:

示例 说明
5a6 在文件 1 第 5 行后添加内容,对应文件 2 第 6 行
3c3 文件 1 第 3 行需要修改为文件 2 第 3 行
9d6 文件 1 第 9 行相关内容需要删除,对应文件 2 第 6 行

准备两个文件:

bash 复制代码
[root@atguigu ~]# cat a.txt
11111111111
22222222222
33333333333
44444444444

[root@atguigu ~]# cat b.txt
11111111111
22222222222
33333333333
aaaaaaaaaaa
bbbbbbbbbbb
ccccccccccc

比较文件差异:

bash 复制代码
[root@atguigu ~]# diff a.txt b.txt
4c4,6
< 44444444444
---
> aaaaaaaaaaa
> bbbbbbbbbbb
> ccccccccccc

解释一下:

  • 4c4,6 表示第一个文件第 4 行需要修改,才能对应第二个文件第 4 到第 6 行。
  • < 44444444444 是第一个文件里的内容。
  • > aaaaaaaaaaa> bbbbbbbbbbb> ccccccccccc 是第二个文件里的内容。

可以把 diff 理解成"告诉你怎么把文件 A 改成文件 B"。如果文件 A 少了内容,就会提示 add;如果文件 A 多了内容,就会提示 delete;如果内容不一样,就会提示 change。

十七、vimdiff:用 vim 方式高亮比较文件

vimdiffvim 编辑器提供的文件差异比较工具,适合做配置对比、脚本版本对比、代码合并等工作。

使用方式:

bash 复制代码
[root@atguigu ~]# vimdiff a.txt b.txt

它的功能类似:

bash 复制代码
vim -d a.txt b.txt

进入后会以 vim 的方式打开两个文件,并高亮显示不同内容。在里面仍然可以使用 vim 操作,比如移动、搜索、保存、退出。

退出时要注意:如果打开了两个窗口,每个窗口都需要退出。可以在每个窗口里执行:

bash 复制代码
:q

如果修改了内容,使用:

bash 复制代码
:wq

如果不想保存修改:

bash 复制代码
:q!

使用场景:比如改了 nginx.conf,想和备份文件对比:

bash 复制代码
vimdiff nginx.conf nginx.conf.bak

相比纯文本 diffvimdiff 更直观,适合人工检查差异。

十八、sort:按行排序

sort 用于针对文本文件内容进行排序,默认以行为单位处理。它默认不会修改原文件,只会把排序后的结果输出到屏幕。

选项 说明
-n 按数值大小排序
-k 按指定列排序
-r 倒序排序,默认是升序
-t '分隔字符' 指定字段分隔符,默认是空格
-u 排序后去重

示例演示:默认排序和数字排序

准备测试文件:

bash 复制代码
[root@atguigu ~]# cat a.txt
100
01
12
23
45
21
36
200
002

直接使用 sort

bash 复制代码
[root@atguigu ~]# sort a.txt
002
01
100
12
200
21
23
36
45

这个结果看起来有点奇怪,原因是 sort 默认按字符排序,不是按数字大小排序。字符排序时,会从左到右比较字符,所以 100 会排在 12 前面。

按数值大小排序,需要加 -n

bash 复制代码
[root@atguigu ~]# sort -n a.txt
01
002
12
21
23
36
45
100
200

示例演示:按第二列排序

修改文件内容:

bash 复制代码
[root@atguigu ~]# cat a.txt
100 a
01 f
12 s
23 w
45 x
21 z
36 e
200 f
002 c

按第二列排序:

bash 复制代码
[root@atguigu ~]# sort -k2 a.txt
100 a
002 c
36 e
01 f
200 f
12 s
23 w
45 x
21 z

因为默认分隔符是空格,所以 sort -k2 能识别第二列。

示例演示:指定分隔符排序

如果文件内容变成下面这样,字段之间用 - 分隔:

bash 复制代码
[root@atguigu ~]# cat a.txt
100-a
01-f
12-s
23-w
45-x
21-z
36-e
200-f
002-c

这时需要用 -t 指定分隔符:

bash 复制代码
[root@atguigu ~]# sort -t '-' -k2 a.txt
100-a
002-c
36-e
01-f
200-f
12-s
23-w
45-x
21-z

按第二列倒序排序:

bash 复制代码
[root@atguigu ~]# sort -t '-' -rk2 a.txt
21-z
45-x
23-w
12-s
200-f
01-f
36-e
002-c
100-a

注意:-r 是倒序,-k2 是按第 2 列排序,-t '-' 是指定 - 作为分隔符。

示例演示:按多列排序

准备学生成绩数据:

bash 复制代码
[root@atguigu ~]# cat a.txt
张三:18:100
李四:25:98
王五:19:100
赵六:25:100
田七:25:90
崔八:19:95
小明:25:85
小红:18:90

按第二列年龄倒序排序,如果年龄相同,再按第三列成绩倒序排序:

bash 复制代码
[root@atguigu ~]# sort -t ':' -rn -k2 -k3 a.txt
赵六:25:100
李四:25:98
田七:25:90
小明:25:85
王五:19:100
崔八:19:95
张三:18:100
小红:18:90

这里的参数含义:

  • -t ':':使用冒号作为分隔符。
  • -r:倒序。
  • -n:按数字排序。
  • -k2 -k3:先按第 2 列,再按第 3 列排序。

这个例子很接近实际业务数据排序。比如按年龄排序、按分数排序、按访问次数排序,本质上都是找准分隔符和字段。

示例演示:IP 地址排序的易错点

准备 IP 和 MAC 数据:

bash 复制代码
[root@atguigu ~]# cat b.txt
192.168.3.1 00:0F:AF:81:19:1F
192.168.3.2 00:0F:AF:85:6C:25
192.168.3.3 00:0F:AF:85:70:42
192.168.2.20 00:0F:AF:85:55:DE
192.168.2.21 00:0F:AF:85:6C:09
192.168.2.22 00:0F:AF:85:5C:41
192.168.0.151 00:0F:AF:85:6C:F6
192.168.0.152 00:0F:AF:83:1F:65
192.168.0.153 00:0F:AF:85:70:03
192.168.1.10 00:30:15:A2:3B:B6
192.168.1.11 00:30:15:A3:23:B7
192.168.1.12 00:30:15:A2:3A:A1
192.168.1.1 00:0F:AF:81:19:1F
192.168.2.2 00:0F:AF:85:6C:25
192.168.3.3 00:0F:AF:85:70:42
192.168.2.20 00:0F:AF:85:55:DE
192.168.1.21 00:0F:AF:85:6C:09
192.168.2.22 00:0F:AF:85:5C:41
192.168.0.151 00:0F:AF:85:6C:F6
192.168.1.152 00:0F:AF:83:1F:65
192.168.0.153 00:0F:AF:85:70:03
192.168.3.10 00:30:15:A2:3B:B6
192.168.1.11 00:30:15:A3:23:B7
192.168.3.12 00:30:15:A2:3A:A1

如果想按 IP 的第三段和第四段倒序排序,容易写成:

bash 复制代码
[root@atguigu ~]# sort -t '.' -rn -k3 -k4 b.txt

这种写法可能出现第三列排序有效,但第四列排序不准确的问题。原因是使用了自定义分隔符后,字段范围如果不写清楚,sort 可能会把后续内容也纳入比较范围。

更稳妥的写法是:

bash 复制代码
[root@atguigu ~]# sort -t '.' -rn -k3,3 -k4,4 b.txt

-k3,3 表示排序字段从第 3 列开始,到第 3 列结束,只比较第 3 列。-k4,4 同理,只比较第 4 列。

这个写法非常重要。以后做多列排序时,特别是分隔符不是默认空格时,建议写成 -k字段,字段,范围更清楚。

示例演示:找 /etc 目录下最大的 10 个文件

bash 复制代码
[root@atguigu ~]# ll /etc | sort -rn -k5 | head
-rw-r--r--.  1 root root   670293 6月   7 2013 services
-rw-r--r--.  1 root root    19710 6月  13 15:20 ld.so.cache
-rw-r--r--.  1 root root    12288 6月  10 15:59 aliases.db
-rw-r--r--.  1 root root     7274 6月  10 15:39 kdump.conf
-rw-r--r--.  1 root root     6545 4月   1 2020 protocols
-rw-r--r--.  1 root root     5725 8月   6 2019 DIR_COLORS.256color
-rw-r--r--.  1 root root     5171 10月 31 2018 man_db.conf
-rw-r--r--.  1 root root     5122 10月  2 2020 makedumpfile.conf.sample
-rw-r--r--.  1 root root     5090 8月   6 2019 DIR_COLORS
-rw-r--r--.  1 root root     4669 8月   6 2019 DIR_COLORS.lightbgcolor

这条命令拆开看:

bash 复制代码
ll /etc          # 列出 /etc 下文件
sort -rn -k5    # 按第 5 列文件大小进行数字倒序排序
head            # 取前 10 行

实际排查磁盘空间时也经常会用类似思路:先列出文件,再按大小排序,再取前几条。

十九、uniq:去掉重复行

uniqunique 的缩写,用于检查和删除文本文件中重复出现的行。

选项 说明
-c 在每行前面显示该行重复出现的次数

示例演示:相邻重复行去重

准备文件:

bash 复制代码
[root@atguigu ~]# cat a.txt
a
a
a
a
b
b
b
c
c
c
c
d
d

使用 uniq 去重:

bash 复制代码
[root@atguigu ~]# uniq a.txt
a
b
c
d

统计重复次数:

bash 复制代码
[root@atguigu ~]# uniq -c a.txt
      4 a
      3 b
      4 c
      2 d

这个例子能正常去重,是因为相同内容都挨在一起:aa 相邻,bb 相邻。

易错点:uniq 只能处理相邻重复行

如果文件内容变成乱序:

bash 复制代码
[root@atguigu ~]# cat a.txt
a
a
a
a
b
b
b
d
c
c
c
a
b
c
a
b
d
c
c
c
d
d

直接使用 uniq

bash 复制代码
[root@atguigu ~]# uniq a.txt
a
b
d
c
a
b
c
a
b
d
c
d

可以看到,结果并没有真正全局去重。原因是 uniq 只会合并相邻重复行,不会在整个文件里"到处找相同内容"。

正确做法是先用 sort 排序,把相同内容排到一起:

bash 复制代码
[root@atguigu ~]# sort a.txt
a
a
a
a
a
a
b
b
b
b
b
c
c
c
c
c
c
c
d
d
d
d

再配合 uniq -c 统计:

bash 复制代码
[root@atguigu ~]# sort a.txt | uniq -c
      6 a
      5 b
      7 c
      4 d

这个组合非常常用:

bash 复制代码
sort a.txt | uniq -c

如果想按出现次数倒序排列,可以继续接一次 sort

bash 复制代码
sort a.txt | uniq -c | sort -rn

二十、组合命令示例

这些命令单独看都不难,但真正好用的是组合起来。

操作示例:统计日志中 ERROR 出现次数

bash 复制代码
grep "ERROR" app.log | wc -l

含义:先筛出包含 ERROR 的行,再统计行数。

操作示例:实时查看服务日志

bash 复制代码
tail -f /var/log/messages

含义:持续查看日志尾部新增内容,排查服务启动失败或系统异常时很常用。

操作示例:查看文件开头和结尾

bash 复制代码
head -n 20 access.log
tail -n 50 access.log

含义:前者看前 20 行,后者看最后 50 行。

操作示例:统计重复 IP

假设 ip.txt 里每行是一个 IP:

bash 复制代码
sort ip.txt | uniq -c | sort -rn

含义:

bash 复制代码
sort ip.txt    # 把相同 IP 排到一起
uniq -c        # 统计每个 IP 出现次数
sort -rn       # 按出现次数倒序排列

操作示例:比较配置文件差异

bash 复制代码
diff nginx.conf nginx.conf.bak
vimdiff nginx.conf nginx.conf.bak

diff 适合快速看文本差异,vimdiff 适合人工打开对比并处理差异。

二十一、注意事项和易错点

  1. vim 打开文件后默认是普通模式,不能直接输入正文。要先按 iao 进入插入模式。

  2. 修改文件后不能直接 :q 退出。需要保存就用 :wq,不保存就用 :q!

  3. .swp 文件不要随手乱删。确认没有其他终端正在编辑同一个文件,再删除。

  4. headtail 默认都是 10 行,一个看开头,一个看结尾。

  5. tail -f 不会自动退出,需要按 Ctrl + C 停止。

  6. 大文件优先用 less,不要直接 cat 刷屏。

  7. sort 默认按字符排序,数字排序要加 -n

  8. 自定义分隔符排序时,用 -t 指定分隔符,用 -k 指定列。

  9. 多列排序时,推荐写成 -k3,3 -k4,4 这种明确字段范围的形式。

  10. uniq 只处理相邻重复行,全局去重通常先 sortuniq

二十二、总结

场景 常用命令
编辑文件 vim
看文件开头 head
看文件结尾 tail
实时看日志 tail -f
翻页看大文件 less / more
统计行数、单词数、字节数 wc
查命令位置 which / whereis
比较文件差异 diff / vimdiff
排序 sort
去重和统计重复次数 uniq