所有文章

Linux - 管道与重定向详解

Unix 哲学

在 Unix 系统中,任何程序都可以实现三个接口,即:标准输入(stdin)、标准输出(stdout)、标准错误输出(stderr),你应该注意到,这三个东西前面都有标准两个字,是的,正是这种标准,使得 Unix 系统中的所有独立的程序可以相互传递数据而没有任何限制,这种机制给用户带来了极大的方便,这正是 Unix 哲学!

什么是管道

在 Unix/Linux 中,有一个常用的符号,叫做管道符,写做:”|“,举个例子:

ls / | grep usr

lsgrep 是两个独立的程序,但是 ls 的输出数据可以传给 grep 继续处理,而管道符”|“在中间起到了数据传输的作用,正如其名,它就像一根橡胶水管,这根水管的两端可以连接任意程序,因为这些程序都向外提供了一组一模一样的接口,这样我们就可以将任意数量的不同功能的程序随意组合起来使用。

什么是重定向

在上例中,管道符的一端连接到 ls 的标准输出,另一端连接到 grep 的标准输入,那我们能不能将其中一端连接到一个文件呢?这样我们就可以将结果数据保存下来,或者将一个文件输入到某个程序中去。当然可以,这时就用到重定向了,下面以 Bash Shell 为例,解释常用重定向符号的意义及其用法。

重定向符号

  1. 0< 标准输入重定向,0可省略
  2. 1> 标准输出重定向,1可省略
  3. 2> 标准错误输出重定向
  4. 它们的用法都是左边给定一个程序,右边给定一个文件,> 表示覆盖,>> 表示追加。

输出重定向

如果我想把一个程序的标准输出追加到 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

创建文件描述符(File Descriptors)

我们还可以自定义一个文件描述符9,并将其绑定到一个管道文件:

mkfifo /tmp/fifo
exec 9<>/tmp/fifo

然后向管道写入数据:

echo `date` >&9

从管道读取数据:

read -u 9 var
echo $var

解除绑定:

exec 9<&-
exec 9>&-

-End-


编写日期:2017-09-21