BASH中pipe道的工作原理是什么?
我经常在BASH中使用pipe道,例如:
dmesg | less
虽然我知道这个输出是什么,但是它需要dmesg
并且让我用less
滚动来浏览它,我不明白是什么 是在做。 这是否与“ >
”相反?
- 对于什么
|
有一个简单的或者隐喻的解释 呢? - 当多条pipe道在一条线上使用时会发生什么?
- BASH脚本中pipe道的行为是否一致?
Unixpipe道将第一个进程的STDOUT(标准输出)文件描述符连接到第二个进程的STDIN(标准input)。 那么会发生什么呢,当第一个进程写入到STDOUT时,第二个进程可以立即读取(从STDIN)输出。
使用多个pipe道与使用单个pipe道没有区别。 每个pipe道都是独立的,只是将相邻进程的STDOUT和STDIN连接起来。
你的第三个问题有点含糊。 是的,pipe道本身在bash脚本中是一致的。 但是,pipe道字符|
可以代表不同的事物。 例如,双pipe( ||
)代表“或”运算符。
Unix中的每个标准进程至less有三个文件描述符 ,它们有点像接口 :
- 标准输出,这是过程打印数据的地方(大部分时间是控制台,即屏幕或terminal)。
- 标准input,它是从中获取数据的地方(大部分时间可能与键盘类似)。
- 标准错误,这是错误和其他带外数据发生的地方。 现在没有意思,因为pipe道通常不会处理它。
pipe道将过程的标准输出连接到左侧的过程的标准input。 您可以将其视为一个专门的程序,负责复制一个程序打印的所有内容,并将其提供给下一个程序(pipe道符号之后的程序)。 这不完全是这样,但是这足够了。
每个pipe道的操作只有两件事:标准输出来自左侧,inputstream预期在右侧。 其中的每一个都可以附加到一个进程或另一个pipe道的位,这是多pipe道命令行中的情况。 但这与pipe道的实际操作无关。 每个pipe道都是自己的。
redirect操作符( >
)做了一些相关的事情,但更简单一些:默认情况下,它将进程的标准输出直接发送到文件。 正如你所看到的,这不是一个pipe道的对立面,但实际上是相辅相成的。 与此相反,并不奇怪,它接受文件的内容并将其发送到进程的标准input(将其视为一个字节读取文件的程序,并将其input到一个进程中)。
在Linux(通常是Unix)中,每个进程都有三个默认的文件描述符:
- fd#0表示进程的标准input
- fd#1表示进程的标准输出
- fd#2表示进程的标准错误输出
通常情况下,当你运行一个简单的程序时,这些文件描述符默认configuration如下:
- 默认input是从键盘读取的
- 标准输出configuration为显示器
- 标准错误也被configuration为监视器
Bash提供了几个运算符来改变这个行为(比如看看>,>>和<运算符)。 因此,您可以将输出redirect到标准输出以外的内容,或者从其他不同于键盘的stream中读取input。 特别有趣的情况下,当两个程序协作的方式,一个使用另一个的输出作为其input。 为了使这种协作变得容易,Bash提供了pipe道操作符| 。 请注意使用协作而不是链接 。 我避免使用这个术语,因为实际上pipe道是不连续的 。 包含pipe道的常规命令行具有以下方面:
> program_1 | program_2 | ... | program_n
上面的命令行有一点误导:用户可能会认为program_2在程序_1执行完成后会得到它的input,这是不正确的。 实际上,bash所做的就是并行启动所有程序,并相应地configurationinput输出,以便每个程序从前一个程序获取input,并将其输出传送到下一个程序(按命令行build立的顺序)。
以下是从创build父进程和subprocess之间的pipe道的C中创buildpipe道的简单示例。 重要的部分是对pipe()的调用以及父级如何closuresfd 1 (写入方)以及孩子如何closuresfd 1 (写入方)。 请注意,pipe道是单向通信通道。 因此,数据只能在一个方向stream动:fd 1到fd [0]。 有关更多信息,请查看pipe()的手册页。
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(void) { int fd[2], nbytes; pid_t childpid; char string[] = "Hello, world!\n"; char readbuffer[80]; pipe(fd); if((childpid = fork()) == -1) { perror("fork"); exit(1); } if(childpid == 0) { /* Child process closes up input side of pipe */ close(fd[0]); /* Send "string" through the output side of pipe */ write(fd[1], string, (strlen(string)+1)); exit(0); } else { /* Parent process closes up output side of pipe */ close(fd[1]); /* Read in a string from the pipe */ nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); printf("Received string: %s", readbuffer); } return(0); }
最后但并非最不重要,当你有一个命令行的forms:
> program_1 | program_2 | program_3
整行的返回码设置为最后一个命令。 在这种情况下,program_3。 如果您想获得中间返回代码,您必须设置pipe道故障或从PIPESTATUS中获取 。
一个pipe道接受一个进程的输出,输出我的意思是标准输出(UNIX上的stdout
),并把它传递给另一个进程的标准input(stdin)
。 它不是简单的右边redirect的目的是将输出redirect到另一个输出。
例如,在Linux上使用echo命令,它只是在标准输出中打印传入参数的string。 如果您使用简单的redirect,如:
echo "Hello world" > helloworld.txt
shell将redirect最初打算在stdout上的正常输出并直接将其打印到文件helloworld.txt
。
现在,以这个涉及pipe道的例子为例:
ls -l | grep helloworld.txt
ls
命令的标准输出将在grep的input处输出,那么这是如何工作的呢?
程序如grep
在没有任何参数的情况下使用时,只是简单地阅读并等待某些东西在标准input(stdin)
上传递。 当他们捕捉到某些东西时,比如ls命令的输出,grep通常会发现正在search的东西。
-
|
把命令的STDOUT放在左边的命令的STDIN右边。 -
如果你使用多个pipe道,那只是一连串的pipe道。 第一个命令输出被设置为第二个命令input。 第二个命令输出设置为下一个命令input。 等等。
-
它在所有基于Linux / widows的命令解释器中都可用。
pipe道操作员获取第一个命令的输出,并通过连接stdin和stdout将其“pipe道”到第二个命令。 在你的例子中,而不是dmesg命令的输出到标准输出(并扔在控制台上),它是直接进入你的下一个命令。
如果您将每个unix命令作为独立模块处理,
但你需要他们使用文字作为一致的接口互相交谈,
怎么办呢?
cmd input output echo "foobar" string "foobar" cat "somefile.txt" file *string inside the file* grep "pattern" "a.txt" pattern, input file *matched string*
你可以说|
是接力马拉松接力棒的比喻。
它甚至形状像一个!
cat -> echo -> less -> awk -> perl
类似于cat | echo | less | awk | perl
cat | echo | less | awk | perl
cat | echo | less | awk | perl
。
cat "somefile.txt" | echo
cat
通过它的输出echo
使用。
当有多个input时会发生什么?
cat "somefile.txt" | grep "pattern"
有一个隐含的规则,说“传递它作为input文件,而不是模式 ”为grep
。
你会慢慢发展,知道哪个参数是经验的眼睛。
这样的pipe道非常简单。
你有一个命令的输出。 您可以使用pipe道将此输出作为input提供给另一个命令。 您可以根据需要input任意数量的命令。
例如:ls | grep my | grep文件
这首先列出工作目录中的文件。 这个输出由grep命令检查单词“my”。 现在输出到第二个grep命令中,最后search单词“files”。 而已。