文件描述符 | 缩写 | 描述 |
---|---|---|
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误 |
STDIN文件描述符代表shell的标准输入。对终端界面来说,标准输入就是键盘。shell会从STDIN文件描述符对应的键盘获得输入并进行处理。在使用输入重定向符(<)时,Linux会用重定向指定的文件替换标准输入文件描述符。于是,命令就会从文件中读取数据,就好像这些数据是从键盘键入的。
STDOUT文件描述符代表shell的标准输出。在终端界面上,标准输出就是终端显示器。shell的所有输出(包括shell中运行的程序和脚本)会被送往标准输出,也就是显示器。
通过输出重定向符(>),原本应该出现在屏幕上的所有输出被shell重定向到了指定的文件。也可以使用>>将数据追加到某个文件:
$ who >> test2
$ cat test2
total 20
-rw-rw-r-- 1 rich rich 53 2020-06-20 11:30 test
-rw-rw-r-- 1 rich rich 0 2020-06-20 11:32 test2
-rw-rw-r-- 1 rich rich 73 2020-06-20 11:23 testfile
rich pts/0 2020-06-20 15:34 (192.168.1.2)
但是,如果对脚本使用标准输出重定向,就会遇到一个问题。来看下面的例子:
$ ls -al badfile > test3
ls: cannot access badfile: No such file or directory
$ cat test3
当命令产生错误消息时,shell并未将错误消息重定向到指定文件。shell创建了输出重定向文件,但错误消息依然显示在屏幕上。注意,在查看test3文件的内容时,里面没有错误消息。test3文件创建成功了,但里面空无一物。shell对于错误消息的处理是跟普通输出分开的。如果你创建了一个在后台运行的shell脚本,则通常必须依赖发送到日志文件的输出消息。用这种方法的话,如果出现错误消息,这些消息也不会出现在日志文件中,因此需要换一种方法来处理。
shell通过特殊的STDERR文件描述符处理错误消息。STDERR文件描述符代表shell的标准错误输出。shell或运行在shell中的程序和脚本报错时,生成的错误消息都会被送往这个位置。
在默认情况下,STDERR和STDOUT指向同一个地方(尽管二者的文件描述符索引值不同)。也就是说,所有的错误消息也都默认会被送往显示器。
如你所见,STDERR并不会随着STDOUT的重定向发生改变。在使用脚本时,你常常会想改变这种行为,尤其是希望将错误消息保存到日志文件中的时候。
如果想重定向错误消息和正常输出,则必须使用两个重定向符号。你需要在重定向符号之前放上需要重定向的文件描述符,然后让它们指向用于保存数据的输出文件:
$ ls -al test test2 test3 badtest 2> test6 1> test7
$ cat test6
ls: cannot access test: No such file or directory
ls: cannot access badtest: No such file or directory
$ cat test7
-rw-rw-r-- 1 rich rich 158 2020-06-20 11:32 test2
-rw-rw-r-- 1 rich rich 0 2020-06-20 11:33 test3
另外,如果愿意,也可以将STDERR和STDOUT的输出重定向到同一个文件。为此,bash shell提供了特殊的重定向符&>。
如果你有意在脚本中生成错误消息,可以将单独的一行输出重定向到STDERR。这只需要使用输出重定向符号将输出重定向到STDERR文件描述符。在重定向到文件描述符时,必须在文件描述符索引值之前加一个&:
echo "This is an error message" >&2
如果脚本中有大量数据需要重定向,那么逐条重定向所有的echo语句会很烦琐。这时可以用exec命令,它会告诉shell在脚本执行期间重定向某个特定文件描述符:
# !/bin/bash
# redirecting output to different locations
exec 2>testerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>testout
echo "This output should go to the testout file"
echo "but this should go to the testerror file" >&2
可以使用与重定向STDOUT和STDERR相同的方法,将STDIN从键盘重定向到其他位置。在Linux系统中,exec命令允许将STDIN重定向为文件:
exec 0< testfile
该命令会告诉shell,它应该从文件testfile中而不是键盘上获取输入。只要脚本需要输入,这个重定向就会起作用。来看一个用法示例:
# !/bin/bash
# redirecting file input
exec 0< state.txt
count=1
while read line
do
echo "Line #$count: $line"
count=$[ $count + 1 ]
done
在脚本中重定向输入和输出时,并不局限于这3个默认的文件描述符。前文提到过,在shell中最多可以打开9个文件描述符。替代性文件描述符从3到8共6个,均可用作输入或输出重定向。这些文件描述符中的任意一个都可以分配给文件并用在脚本中。本节将介绍如何在脚本中使用替代性文件描述符。
可以用exec命令分配用于输出的文件描述符。和标准的文件描述符一样,一旦将替代性文件描述符指向文件,此重定向就会一直有效,直至重新分配。来看一个在脚本中使用替代性文件描述符的简单例子:
# !/bin/bash
# using an alternative file descriptor
exec 3>test13out
echo "This should display on the monitor"
echo "and this should be stored in the file" >&3
echo "Then this should be back on the monitor"
有一个技巧能帮助你恢复已重定向的文件描述符。你可以将另一个文件描述符分配给标准文件描述符,反之亦可。这意味着可以将STDOUT的原先位置重定向到另一个文件描述符,然后再利用该文件描述符恢复STDOUT。这听起来可能有点儿复杂,但实际上并不难。来看一个例子:
# !/bin/bash
exec 3>&1
exec 1>test14out
echo "This should store in the output file"
echo "along with this line."
exec 1>&3
echo "Now things should be back to normal"
可以采用和重定向输出文件描述符同样的办法来重定向输入文件描述符。在重定向到文件之前,先将STDIN指向的位置保存到另一个文件描述符,然后在读取完文件之后将STDIN恢复到原先的位置:
# !/bin/bash
# redirecting input file descriptors
exec 6<&0
exec 0< state.txt
count=1
while read line
do
echo "Line #$count: $line"
count=$[ $count + 1 ]
done
exec 0<&6
read -p "Are you done now? " answer
case $answer in
Y|y) echo "Goodbye";;
N|n) echo "Sorry, this is the end.";;
esac
有时候,你可能不想显示脚本输出。将脚本作为后台进程运行时这很常见。如果在后台运行的脚本出现错误消息,那么shell就会将其通过邮件发送给进程属主。这会很麻烦,尤其是当运行的脚本生成很多烦琐的小错误时。
要解决这个问题,可以将STDERR重定向到一个名为null文件的特殊文件。跟它的名字很像,null文件里什么都没有。shell输出到null文件的任何数据都不会被保存,全部会被丢弃。
在Linux系统中,null文件的标准位置是/dev/null。重定向到该位置的任何数据都会被丢弃,不再显示:
$ ls -al > /dev/null
$ cat /dev/null