shell中的输出重定向

shell中默认有三个标准设备:标准输入(STDIN)、标准输出(STDOUT)、标准错误(STDERR)。

在Linux系统中,一切(或几乎一切)都是文件。因此,标准输入的文件描述符是0,标准输出的文件描述符是1,标准错误的文件描述符是2。

shell命令的输出默认显示在终端显示器上,示例如下:

$ echo hello world
hello world

可以使用输出重定向符号把标准输出重定向到一个文件,示例如下:

$ echo hello world > log.txt
$ cat log.txt
hello world

标准输出的文件描述符为1,该值为输出重定向符号的默认值,可以省略。因此,上面的示例等效为:

$ echo hello world 1> log.txt  (这里的 1 与 > 符号之间不能有空格)
$ cat log.txt
hello world

当然我们也可以使用输出重定向符号对标准错误进行重定向,但必须在 > 符号前明确指定标准错误的文件描述符,即使用 2> 对标准错误进行重定向。

既然提到了输出重定向,就必须说明输出重定向符号有两种:> 符号 和 >> 符号。区别如下:

  • > file:打开file文件时会先清空文件,然后添加输出信息。
  • >> file:打开file文件时不清空文件,直接在file文件结尾处添加输出信息。

示例如下:

$ cat log.txt
hello world
$ >> log.txt
$ cat log.txt
hello world
$ > log.txt
$ cat log.txt
$

如果愿意,可以将STDOUT和STDERR输出重定向到同一个输出文件。为此,bash shell提供了一个特殊的重定向符号,即 &> 符号。

使用 &> 符号,命令生成的所有输出都发送到同一位置,包括数据和错误。而且bash shell自动使错误信息的优先级高于标准输出,这样你就可以一起查看错误信息,而不用在整个输出文件中查找。貌似对cat命令的输出不起作用。

重定向到某个文件描述符

重定向到某个文件描述符时,必须在文件描述符前面添加 & 符号。必须这样的原因:因为我们知道类似1,2这样的文件描述符也是标准的Linux文件名称,添加 & 符号以做区分。

这样一来,我们经常在脚本中见到的 2>&1 命令是不是很好理解了?是的,就是把标准错误重定向到标准输出。这不是废话嘛,标准错误默认就跟标准输出在同一个位置━━终端显示器。对的,使用终端显示器作为输出设备时是这样的,如果我们要把输出重定向一个文件中时,我们就要使用 2>&1 命令了。说到这里,问题来了,如下:

command > file 2> file 与 command > file 2>&1 效果一样吗?

效果貌似一样:因为不管是command产生的标准输出信息还是标准错误信息都重定向到了file文件里。确实如此,但也有让人意料之外的地方:

command > file 2> file 命令把STDOUT和STDERR都直接送到file文件中,file文件会被打开两次,这样STDOUT和STDERR会相互覆盖。该命令执行时相当于两个进程同时向同一个文件中写数据,你写你的,我写我的,也不进行同步,写完拉倒。打开文件一看,数据重叠,乱七八糟。示例如下:

$ cat badfile log.txt > log 2> log
$ cat log
hello world
: No such file or directory
$

command > file 2>&1 命令把STDOUT直接送往file文件,而STDERR经由STDOUT的通道把数据信息送到file文件中。此时,file文件只被打开了一次,因此标准输出数据和标准错误数据不会相互覆盖,而是井然有序。示例如下:

$ cat badfile log.txt > log 2>&1
$ cat log
cat: badfile: No such file or directory
hello world
$

从I/O效率上来说,command > file 2> file 相比于 command > file 2>&1 要低,而且会出现数据相互覆盖的情况。因此,我们一般会使用后面这条命令。

在同一个命令中多次进行输出重定向

如果我们在同一个命令中进行了多次输出重定向操作,会出现什么情况呢?最终命令的输出会重定向到最后一次重定向的位置。读起来很拗口,但实际操作一下就明白了。

$ ls (空目录)
$ tty (查看终端显示器名称)
/dev/pts/7
$ echo hello world > log.txt 1>/dev/pts/7 2>&1
hello world
$ ls
log.txt
$ cat log.txt
$

echo hello world > log.txt 1>/dev/pts/7 2>&1 命令首先把标准输出重定向到log.txt文件,由于当前目录下并不存在该文件,因此会创建该文件,并把文件清空;接着命令又把标准输出重定向到了终端显示器;最后又把标准错误重定向到了标准输出。因此命令的输出还是被发送到了终端显示器上,命令结束,文件关闭,结果log.txt文件里什么数据都没有写入,只是创建了一个空白的文件。

最后说明

最后要说明的是,使用 > 符号进行重定向时,shell并不总是轻松潇洒地清空重定向文件,比如/proc目录下的文件,shell一般会检查要写入的数据,如果要写入的数据或者写入的方式有问题,shell可能会提醒你,也可能不提醒你。

有提示的示例如下:

root@lj:~# cat /proc/sys/net/ipv6/route/max_size
4096
root@lj:~# echo hello > /proc/sys/net/ipv6/route/max_size
-bash: echo: write error: Invalid argument
root@lj:~# cat /proc/sys/net/ipv6/route/max_size
4096
root@lj:~#

没有提示的示例如下:

# cat /proc/sys/net/netfilter/nf_conntrack_udp_timeout
180
# echo hello > /proc/sys/net/netfilter/nf_conntrack_udp_timeout
# cat /proc/sys/net/netfilter/nf_conntrack_udp_timeout
180
#

因此,即便有时候某条命令执行完成后没有任何提示信息,貌似成功了,我们还是应该检查一下相关数据或文件,看看是否真的执行成功了。

上一篇:HihoCoder - 1103 Colorful Lecture Note


下一篇:Asp.net2.0里的SessionPageStatePersister