管道与重定向详解
Unix 哲学
在 Unix 系统中,任何程序都可以实现三个接口,即:标准输入(stdin)、标准输出(stdout)、标准错误输出(stderr),你应该注意到,这三个东西前面都有标准两个字,是的,正是这种标准,使得 Unix 系统中的所有独立的程序可以相互传递数据而没有任何限制,这种机制给用户带来了极大的方便,这正是 Unix 哲学!
什么是管道
在 Unix/Linux 中,有一个常用的符号,叫做管道符,写做:”|“,举个例子:
ls / | grep usr
ls
和 grep
是两个独立的程序,但是 ls
的输出数据可以传给 grep
继续处理,而管道符”|“在中间起到了数据传输的作用,正如其名,它就像一根橡胶水管,这根水管的两端可以连接任意程序,因为这些程序都向外提供了一组一模一样的接口,这样我们就可以将任意数量的不同功能的程序随意组合起来使用。
什么是重定向
在上例中,管道符的一端连接到 ls
的标准输出,另一端连接到 grep
的标准输入,那我们能不能将其中一端连接到一个文件呢?这样我们就可以将结果数据保存下来,或者将一个文件输入到某个程序中去。当然可以,这时就用到重定向了,下面以 Bash Shell 为例,解释常用重定向符号的意义及其用法。
重定向符号
0<
标准输入重定向,0可省略
1>
标准输出重定向,1可省略
2>
标准错误输出重定向
- 它们的用法都是左边给定一个程序,右边给定一个文件,
>
表示覆盖,>>
表示追加。
输出重定向
如果我想把一个程序的标准输出追加到 o.txt
文件中,而错误输出覆盖到 e.txt
文件中,可以这样写:
ls >> o.txt 2> e.txt
把标准输出与错误输出都写入到 o.txt
:
ls &> o.txt
或者像下面这样写
ls > o.txt 2>&1
这两种写法是等价的,但明显第一种更为简洁。
但不能写成下面这样:
ls 2>&1 > o.txt
它不会像你期望的那样执行,这行命令中有两个重定向操作,所有操作符会被从左到右依次解释,首先错误输出中的数据被重定向到标准输出流中,这时标准输出指向的是终端,然后第二个重定向操作将标准输出重定向到了 o.txt
,但这次操作只影响了标准输出,并没有影响错误输出的指向。
上面的解释有些牵强,因为博主实在不知道该怎样翻译,官方解释如下:
directs only the standard output to file dirlist, because the standard error was duplicated from the standard output before the standard output was redirected to dirlist.
输入重定向
将 i.txt
输入到 cat
:
cat < i.txt
将一段文本重定向到 cat
,文本中包含的代码被正常执行:
cat << EOF
hello
The current time:
`date`
EOF
将一段文本重定向到 cat
,文本中所有内容均被视为普通字符串:
cat <<'EOF'
hello
The current time:
`date`
EOF
将一段文本重定向到 cat
,文本中所有内容均被视为普通字符串,且忽略所有前导制表符(也就是date前面的空白部分):
cat <<-'EOF'
hello
The current time:
`date`
EOF
还有一种不常见的输入重定向:
grep Sep <<< `date`
<<<
后面的字符中如果包含代码,会被正常执行,然后再重定向给 grep
。
命名管道
前面讲的管道”|“是没有名字的,这将导致它不能在两个独立的进程间传递数据,这时可以创建一个命名管道来解决。
mkfifo /tmp/fifo
ll /tmp/fifo
prw-r--r-- 1 root root 0 Sep 21 15:59 /tmp/fifo
等待读取数据,如果管道里没有数据,会一直阻塞:
cat /tmp/fifo
另一个进程写入数据:
echo hello > /tmp/fifo
这时cat
命令会后收到echo
命令输出的数据,但当在cat
命令后再执行ctrl + c
命令时,会导致echo
命令退出,如果想随时查看管道中的数据而不影响echo
命令,可以使用下面的方式。
创建文件描述符(File Descriptors)
我们还可以自定义一个文件描述符9,并将其绑定到一个管道文件:
mkfifo /tmp/fifo
exec 9<>/tmp/fifo
然后向管道写入数据:
echo `date` >&9
从管道读取数据:
read -u 9 var
echo $var
解除绑定:
exec 9<&-
exec 9>&-
-End-