Unix命令行文本处理之:管道的艺术
命令行里的“管道工”
在我们这个时代,电脑和手机的图形与音频功能早已超出了70年代终端用户的想象。然而,文本(text)依然是组织和分类文件的核心方式。无论是文件名本身,手机照片中嵌入的GPS坐标,还是音频文件里的元数据,文本在计算的方方面面都扮演着至关重要的角色。幸运的是,Linux 命令行提供了一系列强大的工具来处理文本内容,更棒的是,它还允许我们将这些工具连接起来,创造出更强大的功能。
让我们从一个简单的问题开始:你的 combined.txt 文件里有多少行?wc
(word count,字数统计) 命令可以告诉我们答案。使用 -l
参数,我们可以让它只统计行数(它同样可以统计字符数和单词数):
wc -l combined.txt
类似地,如果你想知道你的主目录(home directory)下有多少个文件和文件夹,可以这样做:
ls ~ > file_list.txt
wc -l file_list.txt
rm file_list.txt
这个方法可行,但为了统计数量而特意创建一个临时文件,用完后马上又删除它,似乎有点小题大做。幸运的是,Unix 命令行提供了一种“捷径”,可以避免创建临时文件。它能将一个命令的输出(称为标准输出 或 STDOUT)直接作为另一个命令的输入(称为标准输入 或 STDIN)。这个过程就像在两个命令之间连接了一根水管,因此,这个操作被称为管道(piping)。
下面演示如何用管道将 ls
命令的输出直接传送给 wc
:
ls ~ | wc -l
请注意,这个过程中没有创建任何临时文件,也不需要指定文件名。管道完全在内存中操作。大多数 Unix 命令行工具,在没有指定输入文件时,都会默认从管道读取数据。
观察上面的命令,它由两个独立的部分组成:ls ~
(列出主目录内容)和 wc -l
(统计行数),它们之间用一个竖线字符 |
分隔开。将命令串联起来的这种做法非常普遍,以至于 |
这个字符本身通常就被称作管道符。
管道符两边的空格主要是为了美观和清晰,并不是必需的。下面这个例子同样有效,它会告诉我们 /etc 目录下的项目数量:
ls /etc|wc -l
哇!这个目录下的文件可真不少。如果我们想把它们全部列出来,肯定会超出整个屏幕的显示范围。之前我们学过,当命令输出内容太多时,最好使用 less
来分页查看。这个技巧在使用管道时同样适用(别忘了,按 q 键退出 less
):
ls /etc | less
构建命令管道
回到我们自己的文件。我们已经知道如何统计 combined.txt 的总行数了,但考虑到这个文件是由几个相同文件多次拼接而成的,我们不禁好奇:其中有多少行是独一无二的呢?
Unix 有一个 uniq
命令,它可以筛选出不重复的行。所以,我们的思路是:先用 cat
命令读出文件内容,然后通过管道传给 uniq
。但我们最终只想要一个计数,所以还需要再接上 wc
。幸运的是,命令行并不限制你只能使用一个管道,我们可以根据需要串联任意多个命令:
cat combined.txt | uniq | wc -l
执行后,你可能会发现得到的结果和文件的总行数相差无几,甚至完全一样。这肯定不对劲吧?为了搞清楚状况,我们可以去掉最后一个管道,直接看看中间步骤的输出。如果文件很长,可以再把它“接”到 less
上方便查看:
cat combined.txt | uniq | less
检查输出后你会发现,大部分重复的行似乎都没有被移除。要理解原因,我们需要查阅一下 uniq
命令的文档。
善用“说明书”:man
命令
大多数命令行工具都附带了一份或长或短的“使用说明书”,我们可以通过 man
(manual,手册) 命令来阅读它。man
命令的输出会自动通过分页器(通常是 less
)显示,所以你可以自由地上下翻页,阅读完毕后按 q 键退出。
man uniq
因为这类文档是通过 man
命令访问的,所以通常被称为“man page”(手册页)。你会经常听到“查一下 man page 获取更多细节”这样的说法。手册页的格式通常很精炼,更像是一份快速参考,而非完整的教程。内容有时会非常技术化,但你通常可以跳过大部分内容,只关注你需要的那个选项或参数的说明。
uniq
的手册页就是一个典型例子。它以一行简短的命令描述开始,接着是使用方法的摘要,然后是每个选项的详细说明。在 DESCRIPTION(描述)部分的第一行,我们就找到了问题的答案:uniq
命令只会处理相邻的(adjacent)重复行。
解决难题:先排序,再筛选
那么,问题就变成了:如何重新排列文件的内容,让所有重复的行都挨在一起呢?答案很简单:对文件内容进行字母排序。
Unix 提供了一个 sort
命令来完成这项工作。快速查看一下 man sort
,我们发现可以直接把文件名作为参数传给它。让我们看看它对我们的文件做了什么:
sort combined.txt | less
现在你应该能看到,文件的所有行都已按字母顺序重新排列,相同的行自然也就挨在了一起。这正是 uniq
命令需要的输入格式!现在,我们可以完成最终的任务了:统计文件中的唯一行数。
sort combined.txt | uniq | wc -l
这个命令链的逻辑是:
sort combined.txt
:读取文件并按行排序,将相同的行聚集在一起。uniq
:接收排序后的数据,删除相邻的重复行。wc -l
:接收uniq
处理后的唯一行数据,并统计其数量。
正如你所见,通过管道将数据从一个命令流向另一个命令,构建起长长的命令链来处理数据,是一种极其强大的技术。它不仅减少了对临时文件的依赖,还为你节省了大量的键盘输入。一个长长的命令链初看起来可能令人生畏,但请记住,你随时可以将其拆解成一个个独立的命令(并查阅它们的手册页),从而更好地理解它的工作原理。
无处不在的手册
几乎所有的 Linux 命令行工具都带有手册页。不妨花点时间看看你已经学过的命令的手册:man ls
、man cp
、man rmdir
等等。甚至 man
命令本身也有手册页,当然,查看它的方式就是 man man
。