一个档案描述符说白了就是档案系统为了跟踪这个开启的档案而分配给它的一个数字,也可以的将其理解为档案指标的一个简单版本,与 C 语言中档案控制代码的概念很相似。
Linux 中预设情况下始终有 3 个 “档案” 处于开启状态,stdin(键盘)、 stdout(萤幕)和 stderr(错误讯息输出到萤幕上)。这 3 个档案和其他开启的档案都可以被重定向。重定向,简单的说就是捕捉一个档案、命令、程式、指令码,或者是指令码中的程式码块的输出,然后将这些输出作为输入传送到另一个档案、命令、程式或指令码中。
每个开启的档案都会被分配一个档案描述符。 stdin 、 stdout 和 stderr 的档案描述符分别是 0 、 1 和 2 。除了这 3 个档案,对于其它那些需要开启的档案,保留了档案描述符 3 到 9 。在某些情况下,将这些额外的档案描述符分配给 stdin 、 stdout 或 stderr 作为临时的副本连结是非常有用的。在经过复杂的重定向和重新整理之后需要把它们恢复成正常状态。
重定向
> file
将 stdout 重定向到一个档案。如果这个档案不存在,那就建立,否则就覆盖。
建立一个包含目录树列表的档案:
ls -lR >dir-tree.list
清空档案:
: > file
这是一个 > 操作,将会把档案 file 变为一个空档案(就是 size 为 0)。如果档案不存在,那么就建立一个 0 长度的档案(与 touch 的效果相同)。: 是一个占位符,不产生任何输出。
也可以省略 : 占位符:
> file
与上边的 : > 效果相同, 但是某些 shell(比如 bash)可能不支援这种形式。
>> file
将 stdout 重定向到一个档案。如果档案不存在,那么就建立它,如果存在,那么就追加到档案后边。
script.sh 1 > filename
# 重定向 stdout 到档案”filename”.
script.sh 1 >> filename
# 重定向并追加 stdout 到档案”filename”.
script.sh 2 > filename
# 重定向 stderr 到档案”filename”.
script.sh 2 >> filename
# 重定向并追加 stderr 到档案”filename”.
&> file
将 stdout 和 stderr 都重定向到档案:
script.sh &> /dev/null
m> file
m 是一个档案描述符,如果没有明确指定的话预设为 1 。
file 是一个档名。档案描述符 m 被重定向到档案 file 。
script.sh 2> error.log
m>&n
m 是一个档案描述符,如果没有明确指定的话预设为 1 。
n 是另一个档案描述符。
script.sh 2>&1
重定向 stderr 到 stdout 。将错误讯息的输出,传送到与标准输出所指向的地方。
exec 6<>File
script.sh >&6
预设的,重定向档案描述符 1(stdout)到 6 。所有传递到 stdout 的输出都送到 6 中去。
< file
从档案中接受输入。与 > 是成对命令,并且通常都是结合使用。 0 < file 或 < file,前面的标准输入 stdin 0 可以省略。
grep search-word < filename
j<>file
为了读写 file,把档案 file 开启,并且将档案描述符 j 分配给它。
如果档案 file 不存在,那么就建立它。如果档案描述符 j 没指定,那预设是标准输入 stdin 0 。
echo 1234567890 > File ### 写字串到 File .
exec 3<>File ### 开启 File 并且将 fd 3 分配给它.
read -n 4 <&3 ### 只读取4个字元.
echo -n . >&3 ### 写一个小数点.
exec 3>&- ### 关闭 fd 3.
cat File ### ==> 1234.67890
(注:上述命令的输出结果和原文不同,原因未知。)
管道
管道与 > 很相似,但是实际上更通用。对于想将命令、指令码、档案和程式串连起来的时候很有用。
cat *.txt | sort | uniq > result-file
上述命令对所有 .txt 档案的输出进行排序,并且删除重复行。最后将结果储存到 result-file 中。
可以将输入输出重定向和/或管道的多个例项结合到一起写在同一行上:
command < input-file > output-file
等价于:
< input-file command > output-file
但是这种写法不标准,有的 shell 可能不支援。
可以将多个输出流重定向到一个档案上:
ls -yz >> command.log 2>&1
将错误选项 yz 的结果放到档案 command.log 中。因为 stderr 被重定向到这个档案中,所以所有的错误讯息也就都指向那里了。
注意,下边这个例子就不会给出相同的结果:
ls -yz 2>&1 >> command.log
输出一个错误讯息,但是并不写到档案中。命令的输出(如果有的话)写入到档案 command.log 。
如果同时将 stdout 和 stderr 都重定向,命令的顺序不同会带来不同的结果。
关闭档案描述符
n<&- 关闭输入档案描述符 n 。
0<&- 或 <&- 关闭 stdin。
n>&- 关闭输出档案描述符 n 。
1>&- 或 >&- 关闭 stdout 。
子程序继承了开启的档案描述符。这就是为什么管道可以工作的原因。如果想阻止档案描述符被继承,那么可以关掉它。
只将 stderr 重定到一个管道。
exec 3>&1 ### 储存当前 stdout 的” 值”(将 fd3 指向 fd0 相同目标)
ls -l 2>&1 >&3 3>&- | grep bad 3>&- ### 对’grep’ 关闭 fd 3
### ^^^^ ^^^^ ###(但不关闭’ls’,正常输出内容不受 grep 影响)
ls -l 2>&1 >&3 | grep bad ### 这样输出内容被转到了 fd3,也不会受 grep 影响
ls badabc -l 2>&1 >&3 |grep bad ### stderr 通过 fd1 输出,会受 grep 影响
exec 3>&- ### 对于剩余的指令码来说,关闭它
使用档案描述符 5 可能会引起问题。当 Bash 使用 exec 建立一个子程序的时候,子程序会继承档案描述符 5
(参考 Chet Ramey 的归档 e-mail: RE: File descriptor 5 is held open)。 最好还是不要去招惹这个特定的档案描述符