器→工具, 工具软件

Linux学习之管道Pipe

钱魏Way · · 0 次浏览

在Linux的shell命名使用中,会经常遇到管道操符,管道操作是一个非常优秀的设计。今天我们就一起深入的学习下。

管道简介

管道,英文为pipe。这是一个我们在学习Linux命令行的时候就会引入的一个很重要的概念。它的发明人是道格拉斯.麦克罗伊,这位也是UNIX上早期shell的发明人。他在发明了shell之后,发现系统操作执行命令的时候,经常有需求要将一个程序的输出交给另一个程序进行处理,这种操作可以使用输入输出重定向加文件搞定,比如:

$ ls  -l /etc/ > etc.txt
$ wc -l etc.txt
234 etc.txt

但是这样未免显得太麻烦了。所以,管道的概念应运而生。目前在任何一个shell中,都可以使用“|”连接两个命令,shell会将前后两个进程的输入输出用一个管道相连,以便达到进程间通信的目的:

$ ls -l /etc/ | wc -l
234

对比以上两种方法,我们也可以理解为,管道本质上就是一个文件,前面的进程以写方式打开文件,后面的进程以读方式打开。这样前面写完后面读,于是就实现了通信。实际上管道的设计也是遵循UNIX的“一切皆文件”设计原则的,它本质上就是一个文件。Linux系统直接把管道实现成了一种文件系统,借助VFS给应用程序提供操作接口。

虽然实现形态上是文件,但是管道本身并不占用磁盘或者其他外部存储的空间。在Linux的实现上,它占用的是内存空间。所以,Linux上的管道就是一个操作方式为文件的内存缓冲区。

基本概念:

  • 管道是一种特殊的文件类型,允许一个进程的输出直接作为另一个进程的输入。
  • 管道在Unix中通常用竖线符号| 表示。

工作原理:

  • 当创建一个管道时,操作系统会创建一个缓冲区,用于存储从管道一端传入的数据,直到另一端的进程读取这些数据。
  • 管道是单向的,数据只能从一个方向流动。

使用场景:

  • 管道广泛用于Unix中的命令行操作,通过管道可以将多个命令串联起来,形成一个命令链。
  • 例如,ls | grep “txt”命令会先列出当前目录的所有文件和目录,然后通过管道传递给 grep 命令,后者只输出包含 “txt” 的行。

优点:

  • 简化了进程间的通信。
  • 增加了命令的灵活性和强大的组合能力。
  • 促进了小型、专用程序的使用。

局限性:

  • 由于管道是单向的,为了实现双向通信,需要创建两个管道。
  • 管道中的数据是暂时的,一旦被读取,就会从管道中消失。

如果说Unix是计算机文明中最伟大的发明,那么,Unix下的Pipe管道就是跟随Unix所带来的另一个伟大的发明。管道所要解决的问题,还是软件设计中老生常谈的设计目标——高内聚,低耦合。它以一种“链式模型”来串接不同的程序或者不同的组件,让它们组成一条直线的工作流。管道是一种高效的进程间通信机制,它通过连接多个命令和程序,使得用户可以构建复杂的数据处理操作。

重定向和管道的区别

乍看起来,管道也有重定向的作用,它也改变了数据输入输出的方向,那么,管道和重定向之间到底有什么不同呢?简单地说:

  • 重定向操作符>将命令与文件连接起来,用文件来接收命令的输出;
  • 管道符|将命令与命令连接起来,用第二个命令来接收第一个命令的输出。

如下所示:

command > file
command1 | command1

管道命令

学习管道之前我们先了解一下linux的命令执行顺序。通常情况下,我们在终端只能执行一条命令,然后按下回车执行,那么如何执行多条命令呢?

  • 顺序执行多条命令:command1;command2;command3;简单的顺序指令可以通过 ;来实现
  • 有条件的执行多条命令:which command1 && command2 || command3
    • && : 如果前一条命令执行成功则执行下一条命令,如果command1执行成功(返回0),则执行command2
    • || :与&&命令相反,执行不成功时执行这个命令
  • $?: 存储上一次命令的返回结果

举例:

$ which git>/dev/null && git --help  // 如果存在git命令,执行git --help命令
$ echo $? 

管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。管道命令使用|作为界定符号,管道命令与上面说的连续执行命令不一样。

  • 管道命令仅能处理standard output,对于standard error output会予以忽略。less,more,head,tail…都是可以接受standard input的命令,所以他们是管道命令。ls,cp,mv并不会接受standard input的命令,所以他们就不是管道命令了。
  • 管道命令必须要能够接受来自前一个命令的数据成为standard input继续处理才行。

第一个管道命令

$ ls -al /etc | less

通过管道将ls -al的输出作为 下一个命令less的输入,方便浏览。

以less结束的管道(或more,这是个相似的标签页工具)是最常被使用的。这让用户可以阅览尚未显示的大量文字(受可用缓存限制,控制台的屏幕大小、屏幕缓存大小往往有限,不足以一次先输出所有输出内容,也不能自由滚动内容),若少了这工具则这些文字将会卷过终端而无法阅读到。换句话说,他们将程序员从为自己的软件开发分页器的负担中解放了出来:他们只需要把他们的输出用过“管道”导入到less程序中即可,甚至也可以完全不顾分页问题,去假定他们的用户会在需要将输出分页的时候自己去这样做。

选取命令:cut.grep

cut:从某一行信息中取出某部分我们想要的信息。

  • cut -d ‘分隔字符’ -f field:根据-d 将信息分隔成数段,-f后接数字表示取出第几段
  • cut -c 字符范围。以字符为单位取出固定字符区间的信息
##打印/etc/passwd文件中以:为分隔符的第1个字段和第6个字段分别表示用户名和家目录
# cat etc/passwd | cut -d ':' -f 1,6
##打印/etc/passwd文件中每一行的前10个字符:
# cat /etc/passwd | cut -c 1-10

grep:分析一行信息,如果其中有我们需要的信息,就将该行拿出来

grep [-acinv] [–color=auto] ‘查找字符串’ filename

  • -a : 将binary文件以text文件的方式查找数据
  • -c : 计算找到 ‘查找字符串’的次数
  • -i : 忽略大小写的不同
  • -n : 输出行号
    -v : 反向选择,显示没有查找内容的行
  • –color=auto : 将找到的关键字部分加上颜色显示

示例:

##取出含有 fanco 的/etc/passwd文件的行
# cat etc/passwd | grep -n -c 'fanco'
# cat etc/passwd | grep -n 'fanco'
# cat etc/passwd | grep -n -v 'fanco'

排序命令:sort,wc,uniq

sort

sort [-fbMnrtuk] [file or stdin]

  • -f :忽略大小写的差异,例如A 与a 视为编码相同
  • -b :忽略最前面的空格部分
  • -M :以月份的名字来排序,例如JAN, DEC 等等的排序方法
  • -n :使用『纯数字』进行排序默认是以文字型态来排序的)
  • -r :反向排序
  • -u :就是uniq ,相同的资料中,仅出现一行代表
  • -t :分隔符号,预设是用[tab] 键来分隔
  • -k :以那个区间(field) 来进行排序的意思

示例:

##对/etc/passwd的账号进行排序
# cat /etc/passwd | sort
##通过/etc/passwd 第5列来进行排序
# cat etc/passwd | sort -t ':' -k 3
##这里排序还是按照文字进行排序的,切换成数字排序
# cat etc/passwd | sort -t ':' -k 3 -n

uniq

uniq [-ic]

  • -i :忽略大小写的不同
  • -c :进行计数

示例:

##使用 last 取出历史登录信息的账号,排序,去重
# last | cut -d ' ' -f 1 | sort | uniq -c

wc

  • wc [-lwm]
  • -l :仅列出行
  • -w :仅列出多少字(英文单字)
  • -m :多少字符

示例:

##查看etc/passwd中有多少账号
# cat /etc/passwd | wc -l
##计算最近登录系统的人次
# last | grep [a-zA-Z] | grep -v 'wtmp' | wc -l
##查看某个文件的行数 字数 字符数
# cat etc/passwd | wc

双向重定向命令:tee

tee:在数据流的处理过程中将某段信息保存下来,使其既能输出到屏幕又能保存到某一个文件中。

tee [-a] file

  • -a : 以累加的方式,将数据加入file中

示例:

##查询最近用户登录情况,并将其保存到文件中
# last | tee info | cut -d ' ' -f 1
# less info

如果tee后接的文件已存在,内容会被覆盖掉,加上 -a参数则会累加

字符转换命令:tr,col,join,paste,expand

tr:用来删除一段信息当中的文字,或者进行文字信息得替换

tr [-ds] set

  • -d : 删除信息当中的set1这个字符串
  • -s : 替换掉重复的字符

示例:

##将上一步生成的info 文件删除掉所有的 root
# cat info | tr -d 'root'  
##删除时并不是只删除连续的字符,reboot也被删除掉了root部分
##除去dos文件留下来的^M符号,^M可以用\r替代
$ cat /root/passwd | tr -d '\r' > /root/passwd.linux

col

col经常被用于将man page转存为纯文本文件

col [-xb]

  • -x : 将tab键换成对等的空格键
  • -b : 在文字内有反斜杠(/)时,仅保留反斜杠最后接的那个字符

示例:

##将上图中的^I换成空格键
# cat info | col -x | cat -A | more

join:主要讲两个文件有相同数据的一行,相同字段放在前面

join [-ti12] file1 file2

  • -t : join 默认以空格符分隔数据,并且对比第一个字段的数据 ,如果两个文件相同,则将两条数据连成一行
  • -i : 忽略大小写的差异
  • -1 : 说明第一个文件通过那个字段来进行分析
  • -2 : 说明第二个文件通过那个字段来分析

示例:

##将/etc/passwd 与  /etc/shadow 相关数据整合成一列
# head -3 /etc/passwd /etc/shadow
##将etc/passwd 按:分隔的第4个字段 与 etc/group的第3个字段 比较,如果相同,则将他两同行数据放在一起
# join -t ':' -1 4 /etc/passwd -2 3 /etc/group

paste:直接将两个文件两行贴在一起,中间以[tab]键隔开

paste [-d] file1 file2

  • -d : 后面可以接分隔字符,默认以[tab]来分隔的
  • -: 如果file部分写成-,表示接受standard input数据的意思

示例:

# paste info info2

expand:把tab键转为空格键

expand [-t] file

  • -t : 后面接数字,一般,一个tab可以用8个空格代替,可以自行定义代表几个空格

示例:

# cat info | expand -3 info

切割命令:split

split:顾名思义,讲一个大文件依据文件大小或行数切割成为小文件

  • split [-bl] file prefix
  • -b : 后面可接欲切割文件的大小,可加单位,例如b,k,m等
  • -l : 以行数来进行切割
  • PREFIX : 代表前导符,可作为切割文件的前导文字

示例:

$ split -b 300K /etc/passwd

管道的原理

Shell中通过fork+exec创建子进程来执行命令。如果是含管道的Shell命令,则管道前后的命令分别由不同的进程执行,然后通过管道把两个进程的标准输入输出连接起来,就实现了管道。

例如:

grep "error" minicom.log | awk '{print $1}'

这句命名的作用非常简单,

  • 通过grep命令在log中检索含有error关键字的行
  • 通过awk命令打印grep的输出结果中每一行的第一个字段

在Shell中要实现这样的效果,有4个步骤:

  • 创建pipe
  • fork两个子进程执行grep和awk命令
  • 把grep子进程的标准输出、标准错误重定向到管道数据入口
  • 把awk子进程的标准输入重定向到管道数据出口

这样就实现了Shell管道:grep把结果输出到管道,awk从管道获取数据。

PIPE和FIFO

PIPE(管道)和FIFO(先进先出队列)都是在Unix和类Unix系统中用于进程间通信的机制,但它们在工作方式和用途上有一些区别:

PIPE(管道)

定义:PIPE是一种典型的Unix进程间通信机制,允许数据流从一个进程流向另一个进程。

特点:

  • 通常是匿名的,即没有具体的文件名与之关联。
  • 主要用于父子进程或者相关进程之间的通信。
  • 是半双工的通信方式,数据只能单向流动。
  • 通常在命令行中使用管道符号 | 创建,例如 ls | grep txt。

FIFO(先进先出队列)

定义:FIFO,也称为命名管道,是一种特殊类型的文件,用于在不相关的进程之间进行通信。

特点:

  • 与PIPE不同,FIFO有具体的文件名,位于文件系统中。
  • 可以被不相关的进程打开进行读写,实现进程间通信。
  • 也是半双工的通信方式,但可以通过创建两个FIFO文件来实现全双工通信。
  • 使用 mkfifo 命令在Unix系统中创建。

对比

  • 应用场景:PIPE通常用于有关联的进程(如父子进程)之间的通信,而FIFO用于不相关的进程间通信。
  • 创建方式:PIPE是在进程创建时由操作系统隐式创建的,通常用于临时通信;FIFO是显式创建在文件系统中的,适用于更持久的通信需求。
  • 命名:PIPE一般是匿名的,而FIFO有具体的文件名。
  • 通信方式:两者都是半双工通信,但FIFO可以通过创建两个命名管道来模拟全双工通信。

总的来说,虽然PIPE和FIFO在基本原理上相似(都是基于管道的通信机制),它们在使用场景和实现方式上各有特点。PIPE适用于临时、快速的通信,特别是在有关联的进程之间;而FIFO则更适用于长期、稳定的通信,特别是在不相关的进程之间。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注