Linux 系统每次读写文件时,都从文件描述符下手,通过文件描述符找到文件指针,然后进入打开文件表和 i-node 表,打开文件表和i-node表中保存了与打开文件相关的各种信息。
文件指针是一个内存地址,是文件描述符和真实文件之间最关键的“纽带”,当我们改变了文件指针的指向,就可以改变文件描述符对应的真实文件,比如文件描述符 1 本来对应显示器,但是我们偷偷将文件指针指向了 log.txt 文件,那么文件描述符 1 也就和 log.txt 对应起来了。
Linux 系统提供的函数可以修改文件指针,比如 dup()、dup2();Shell 也能通过重定向修改文件指针,在发生重定向时,Linux 会用文件描述符表(一个结构体数组)中的一个元素给另一个元素赋值,或者用一个结构体变量给数组元素赋值,文件描述符并没有改变,改变的是文件描述符对应的文件指针。对于标准输出,Linux 系统始终向文件描述符 1 中输出内容,而不管它的文件指针指向哪里;只要我们修改了文件指针,就能向任意文件中输出内容。
以下面的语句为例来说明:
echo "c.biancheng.net" 1>log.txt
文件描述符表本质上是一个结构体数组,假设这个结构体的名字叫做 FD。发生重定向时,Linux 系统首先会打开 log.txt 文件,并把各
种信息添加到 i-node 表和文件打开表,然后再创建一个 FD 变量(通过这个变量其实就能读写文件了),并用这个变量给下标为 1 的
数组元素赋值,覆盖原来的内容,这样就改变了文件指针的指向,完成了重定向。
n>&m:实际意义:修改n对应的文件描述符使其与m一致,最总n与m指向同一个
n>file:实际意义:修改n对应的文件描述符使其与file一致,最总n也指向file
3>&1
表示fd=3复制于fd=1,而fd=1目前的重定向目标文件是/dev/stdout,因此fd=3也重定向到/dev/stdout,以后进程将数据写入fd=3的时候,将直接输出到屏幕。
这里的3>&1
等价于3>&/dev/stdout
。如果用”复制”来理解,就是fd=3是当前fd=1的一个副本,即指向/dev/stdout设备。如果后面改变了fd=1的输出目标(如file1),由于fd=3的目标仍然是/dev/stdout,所以可以拿fd=3来还原fd=1使其目标变回/dev/stdout。
command >file 2>&1等价于&>file
表示标准输出和标准错误都重定向到file中
先打开file,再将fd=1重定向到file文件上,这样file文件就成了标准输出的输出目标;之后再将fd=2复制于fd=1,而fd=1此时已经重定向到file文件上,因此fd=2也重定向到file上。所以,最终的结果是标准输出重定向到file上,标准错误也重定向到file上。
command 2>&1 >file:
Shell 会先执行 2>&1,这样 1 和 2 都指向了标准错误输出文件,也即显示器;接着执行 1>file,这样 1 就指向了 file 文件,但是 2依然指向显示器。最终的结果是,正确的输出结果输出到了 file 文件,错误信息却还是输出到显示器