几种常见的文件类型
另一方面,Linux 下的目录也是一种文件;但是文件也不只有目录和可执行文件两种。常见的文件类型有以下 7 种:
普通文件(比如一个文本文件);
目录文件(目录也是一个特殊的文件,它用来存储文件清单,比如/也是一个文件);
可执行文件(上面的rm就是一个可执行文件);
管道文件(我们会在 07 课时讨论管道文件);
Socket 文件(我们会在模块七网络部分讨论 Socket 文件);
软链接文件(相当于指向另一个文件所在路径的符号);
硬链接文件(相当于指向另一个文件的指针,关于软硬链接我们将在模块六文件系统部分讨论)。
设备文件
Socket 是网络插座,是客户端和服务器之间同步数据的接口。其实,Linux 不只把 Socket 抽象成了文件,设备基本也都被抽象成了文件。因为设备需要不断和操作系统交换数据。而交换方式只有两种——读和写。所以设备是可以抽象成文件的,因为文件也支持这两种操作。
Linux 把所有的设备都抽象成了文件,比如说打印机、USB、显卡等。这让整体的系统设计变得高度统一。
至此,我们了解了 Linux 对文件目录的抽象,接下来我们看看具体的增删改查指令。
head/tail
head和tail是一组,它们用来读取一个文件的头部 N 行或者尾部 N 行。比如一个线上的大日志文件,当线上出了 bug,服务暂停的时候,我们就可以用tail -n 1000去查看最后的 1000 行日志文件,寻找导致服务异常的原因。
另一个比较重要的用法是,如果你想看一个实时的nginx日志,可以使用tail -f 文件名,这样你会看到用户的请求不断进来。查一下man,你会发现-f是 follow 的意思,就是文件追加的内容会跟随输出到标准输出流。
grep(这个命令是重点)
有时候你需要查看一个指定ip的nginx日志,或者查看一段时间内的nginx日志。如果不想用less和more进入文件中去查看,就可以用grep命令。Linux 的文件命名风格都很短,所以也影响了很多人,比如之前我看到过一个大牛的程序,变量名从来不超过 5 个字母,而且都有意义。
grep 这个词,我们分成三段来看,是 g|re|p。
g 就是 global,全局;
re 就是 regular expression,正则表达式;
p 就是 pattern,模式。
所以这个指令的作用是通过正则表达式全局搜索一个文件找到匹配的模式。我觉得这种命名真的很牛,软件命名也是一个世纪难题,grep这个名字不但发音不错,而且很有含义,又避免了名字过长,方便记忆。
下面我们举两个例子看看 grep 的用法:
find
find 指令帮助我们在文件系统中查找文件。 比如我们如果想要查找所有.txt 扩展名的文件,可以使用find / -iname "*.txt",-iname这个参数是用来匹配查找的,i 字母代表忽略大小写,这里也可以用-name替代。输入这条指令,你会看到不断查找文件,如下图所示:
grep指令搜索文件内容。
find指令全局查找文件。
进程管理
top
另外还有一个和ps能力差不多,但是显示的不是快照而是实时更新数据的top指令。因为自带的top显示的内容有点少, 所以我喜欢用一个叫作htop的指令,具体的安装全方法我会在 10 | 软件的安装: 编译安装和包管理器安装有什么优势和劣势?中给你介绍。本课时,我们先看一下使用效果,如下图所示:
管道(Pipeline)
现在你已经掌握了一点点进程的基础,下面我们来学习管道,管道(Pipeline)的作用是在命令和命令之间,传递数据。比如说一个命令的结果,就可以作为另一个命令的输入。我们了解了进程,所以这里说的命令就是进程。更准确地说,管道在进程间传递数据。
输入输出流
每个进程拥有自己的标准输入流、标准输出流、标准错误流。
这几个标准流说起来很复杂,但其实都是文件。
标准输入流(用 0 表示)可以作为进程执行的上下文(进程执行可以从输入流中获取数据)。
标准输出流(用 1 表示)中写入的结果会被打印到屏幕上。
如果进程在执行过程中发生异常,那么异常信息会被记录到标准错误流(用 2 表示)中。
重定向
我们执行一个指令,比如ls -l,结果会写入标准输出流,进而被打印。这时可以用重定向符将结果重定向到一个文件,比如说ls -l > out,这样out文件就会有ls -l的结果;而屏幕上也不会再打印ls -l的结果。
具体来说>符号叫作覆盖重定向;>>叫作追加重定向。>每次都会把目标文件覆盖,>>会在目标文件中追加。比如你每次启动一个程序日志都写入/var/log/somelogfile中,可以这样操作,如下所示:
start.sh >> /var/log/somelogfile
经过这样的操作后,每次执行程序日志就不会被覆盖了。
另外还有一种情况,比如我们输入:
ls1 > out
结果并不会存入out
文件,因为ls1
指令是不存在的。结果会输出到标准错误流中,仍然在屏幕上。这里我们可以把标准错误流也重定向到标准输出流,然后再重定向到文件。
ls1 &> out
这个写法等价于:
ls1 > out 2>&1
相当于把ls1
的标准输出流重定向到out
,因为ls1 > out
出错了,所以标准错误流被定向到了标准输出流。&
代表一种引用关系,具体代表的是ls1 >out
的标准输出流。
管道的作用和分类
有了进程和重定向的知识,接下来我们梳理下管道的作用。管道(Pipeline)将一个进程的输出流定向到另一个进程的输入流,就像水管一样,作用就是把这两个文件接起来。如果一个进程输出了一个字符 X,那么另一个进程就会获得 X 这个输入。
管道和重定向很像,但是管道是一个连接一个进行计算,重定向是将一个文件的内容定向到另一个文件,这二者经常会结合使用。
Linux 中的管道也是文件,有两种类型的管道:
-
匿名管道(Unnamed Pipeline),这种管道也在文件系统中,但是它只是一个存储节点,不属于任何一个目录。说白了,就是没有路径。
-
命名管道(Named Pipeline),这种管道就是一个文件,有自己的路径。
FIFO
管道具有 FIFO(First In First Out),FIFO 和排队场景一样,先排到的先获得。所以先流入管道文件的数据,也会先流出去传递给管道下游的进程。
使用场景分析
接下来我们以多个场景举例帮助你深入学习管道。
排序
比如我们用ls
,希望按照文件名排序倒序,可以使用匿名管道,将ls
的结果传递给sort
指令去排序。你看,这样ls
的开发者就不用关心排序问题了。
去重
另一个比较常见的场景是去重,比如有一个字典文件,里面都是词语。如下所示:
Apple
Banana
Apple
Banana
……
筛选
有时候我们想根据正则模式筛选对应的内容。比如说我们想找到项目文件下所有文件名中含有Spring
的文件。就可以利用grep
指令,操作如下:
find ./ | grep Spring
find ./
递归列出当前目录下所有目录中的文件。grep
从find
的输出流中找出含有Spring
关键字的行。
如果我们希望包含Spring
但不包含MyBatis
就可以这样操作:
find ./ | grep Spring | grep -v MyBatis
rep -v
是匹配不包含 MyBatis 的结果。
数行数
还有一个比较常见的场景是数行数。比如你写了一个 Java 文件想知道里面有多少行,就可以使用wc -l
指令,如下所示: