进程、重定向和管道指令:xargs 指令的作用是?

进程

为了弄清楚这节课程的内容,也就是管道,我们先来讨论一下进程。

我们知道,应用的可执行文件是放在文件系统里,把可执行文件启动,就会在操作系统里(具体来说是内存中)形成一个应用的副本,这个副本就是进程。

插一个小知识,以后你再遇到面试题:什么是进程?
\color{green}{可以回答:进程是应用的执行副本;而不要回答进程是操作系统分配资源的最小单位。前者是定义,后者是作用。}

ps

如果你要看当前的进程,可以用ps指令。p 代表 processes,也就是进程;s 代表 snapshot,也就是快照。所谓快照,就是像拍照一样。


1.png

如上图所示,我启动了两个进程,ps和bash。ps 就是我刚刚启动的,被ps自己捕捉到了;bash是因为我开了这个控制台,执行的shell是bash。

当然操作系统也不可能只有这么几个进程,这是因为不带任何参数的ps指令显示的是同一个电传打字机(TTY上)的进程。TTY 这个概念是一个历史的概念,过去用来传递信息,现在已经被传真、邮件、微信等取代。

操作系统上的 TTY 是一个输入输出终端的概念,比如用户打开 bash,操作系统就为用户分配了一个输入输出终端。没有加任何参数的ps只显示在同一个 TTY 的进程。

如果想看到所有的进程,可以用ps -e,-e没有特殊含义,只是为了和-A区分开。我们通常不直接用ps -e而是用ps -ef,这是因为-f可以带上更多的描述字段,如下图所示:


02.png
  • UID 指进程的所有者;

  • PID 是进程的唯一标识;

  • PPID 是进程的父进程 ID;

  • C 是 CPU 的利用率(就是 CPU 占用);

  • STIME 是开始时间;

  • TTY 是进程所在的 TTY,如果没有 TTY 就是 ?号;

  • TIME;

  • CMD 是进程启动时的命令,如果不是一个 Shell 命令,而是用方括号括起来,那就是系统进程或者内核过程。

另外一个用得比较多的是ps aux,它和ps -ef能力差不多,但是是 BSD 风格的。就是加州伯克利分校研发的 Unix 分支版本的衍生风格,这种风格其实不太好描述,我截了一张图,你可以体会一下:


03.png

top
另外还有一个和ps能力差不多,但是显示的不是快照而是实时更新数据的top指令。因为自带的top显示的内容有点少, 所以我喜欢用一个叫作htop的指令,我们先看一下使用效果,如下图所示:

04.png

管道(Pipeline)

现在你已经掌握了一点点进程的基础,下面我们来学习管道,管道(Pipeline)的作用是在命令和命令之间,传递数据。比如说一个命令的结果,就可以作为另一个命令的输入。我们了解了进程,所以这里说的命令就是进程。更准确地说,管道在进程间传递数据。

输入输出流
每个进程拥有自己的标准输入流、标准输出流、标准错误流。

这几个标准流说起来很复杂,但其实都是文件。

  • 标准输入流(用 0 表示)可以作为进程执行的上下文(进程执行可以从输入流中获取数据)。

  • 标准输出流(用 1 表示)中写入的结果会被打印到屏幕上。

  • 如果进程在执行过程中发生异常,那么异常信息会被记录到标准错误流(用 2 表示)中。

重定向

我们执行一个指令,比如ls -l,结果会写入标准输出流,进而被打印。这时可以用重定向符将结果重定向到一个文件,比如说ls -l > out,这样out文件就会有ls -l的结果;而屏幕上也不会再打印ls -l的结果。


05.png

具体来说>符号叫作覆盖重定向;>>叫作追加重定向。>每次都会把目标文件覆盖,>>会在目标文件中追加。比如你每次启动一个程序日志都写入/var/log/somelogfile中,可以这样操作,如下所示:

start.sh >> /var/log/somelogfile

经过这样的操作后,每次执行程序日志就不会被覆盖了。
另外还有一种情况,比如我们输入:

ls1 > out

结果并不会存入out文件,因为ls1指令是不存在的。结果会输出到标准错误流中,仍然在屏幕上。这里我们可以把标准错误流也重定向到标准输出流,然后再重定向到文件。

ls1 &> out

这个写法等价于:

ls1 > out 2>&1
06.png

相当于把ls1的标准输出流重定向到out,因为ls1 > out出错了,所以标准错误流被定向到了标准输出流。&代表一种引用关系,具体代表的是ls1 >out的标准输出流。
管道的作用和分类
有了进程和重定向的知识,接下来我们梳理下管道的作用。管道(Pipeline)将一个进程的输出流定向到另一个进程的输入流,就像水管一样,作用就是把这两个文件接起来。如果一个进程输出了一个字符 X,那么另一个进程就会获得 X 这个输入。

管道和重定向很像,但是管道是一个连接一个进行计算,重定向是将一个文件的内容定向到另一个文件,这二者经常会结合使用。

Linux 中的管道也是文件,有两种类型的管道:

1、匿名管道(Unnamed Pipeline),这种管道也在文件系统中,但是它只是一个存储节点,不属于任何一个目录。说白了,就是没有路径。

2、命名管道(Named Pipeline),这种管道就是一个文件,有自己的路径。

FIFO
管道具有 FIFO(First In First Out),FIFO 和排队场景一样,先排到的先获得。所以先流入管道文件的数据,也会先流出去传递给管道下游的进程。
使用场景分析
接下来我们以多个场景举例帮助你深入学习管道。

排序
比如我们用ls,希望按照文件名排序倒序,可以使用匿名管道,将ls的结果传递给sort指令去排序。你看,这样ls的开发者就不用关心排序问题了。

07.png

去重
另一个比较常见的场景是去重,比如有一个字典文件,里面都是词语。如下所示:

Apple
Banana
Apple
Banana

如果我们想要去重可以使用uniq指令,uniq指令能够找到文件中相邻的重复行,然后去重。但是我们上面的文件重复行是交替的,所以不可以直接用uniq,因此可以先sort这个文件,然后利用管道将sort的结果重定向到uniq指令。指令如下:

08.png

筛选
有时候我们想根据正则模式筛选对应的内容。比如说我们想找到项目文件下所有文件名中含有Spring的文件。就可以利用grep指令,操作如下:

find ./ | grep Spring

find ./递归列出当前目录下所有目录中的文件。grep从find的输出流中找出含有Spring关键字的行。
如果我们希望包含Spring但不包含MyBatis就可以这样操作:

find ./ | grep Spring | grep -v MyBatis

grep -v是匹配不包含 MyBatis 的结果。

数行数
还有一个比较常见的场景是数行数。比如你写了一个 Java 文件想知道里面有多少行,就可以使用wc -l指令,如下所示:

11.png

但是如果你想知道当前目录下有多少个文件,可以用ls | wc -l,如下所示:


12.png

接下来请你思考一个问题:我们如何知道当前java的项目目录下有多少行代码?

提示一下。你可以使用下面这个指令:

find -i ".java" ./ | wc -l

中间结果

管道一个接着一个,是一个计算逻辑。有时候我们想要把中间的结果保存下来,这就需要用到tee指令。tee指令从标准输入流中读取数据到标准输出流。

这时候,你可能会问: 这不是什么都没做吗?

别急,tee还有一个能力,就是自己利用这个过程把输入流中读取到的数据存到文件中。比如下面这条指令:

find ./ -i "*.java" | tee JavaList | grep Spring

这句指令的意思是从当前目录中找到所有含有 Spring 关键字的 Java 文件。tee 本身不影响指令的执行,但是 tee 会把 find 指令的结果保存到 JavaList 文件中。

tee这个执行就像英文字母中的 T 一样,连通管道两端,下面又开了口。这个开口,在函数式编程里面叫作副作用。

你可能感兴趣的:(进程、重定向和管道指令:xargs 指令的作用是?)