玩转文件描述符及重定向
文件描述符是与文件输入、输出相关联的整数。它们用来跟踪已打开的文件。最常见的文件 描述符是stdin、stdout和stderr。我们甚至可以将某个文件描述符的内容重定向到另一个文 件描述符中。下面给出一些对文件描述符进行操作和重定向的例子。
在编写脚本的时候会频繁使用标准输入(stdin)、标准输出(stdout)和标准错误 (stderr)。通过内容过滤将输出重定向到文件是我们平日里的基本任务之一。当命令输出文本 时,这些输出文本有可能是错误信息,也可能是正常的(非错误的)输出信息。单靠查看输出的
文本本身,我们没法区分哪些是正常,哪些是错误。不过可以通过文件描述符来解决这个问题,
将那些与特定描述符关联的文本提取出来。
文件描述符是与某个打开的文件或数据流相关联的整数。文件描述符0、1以及2是系统预留的。
0 —— stdin(标准输入)。 1 —— stdout(标准输出)。 2 —— stderr(标准错误)。
实例
(1) 将输出文本重定向或保存到一个文件中:
echo "This is a sample text 1" > temp.txt
这种方法通过截断文件的方式,将输出文本存储到文件temp.txt中,也就是说在把 echo命令的输出写入文件之前,temp.txt中的内容首先会被清空。
(2) 将文本追加到目标文件中
echo "This is sample text 2" >> temp.txt
>cat temp.txt
This is sample text 1
This is sample text 2
(3) 标准错误以及如何对它重定向。当命令输出错误信息时,stderr信息 就会被打印出来。考虑下面的例子:
$ ls +
ls: cannot access +: No such file or directory
这里,+是一个非法参数,因此将返回错误信息。
-------------------------------------------------------------------------------
下面的命令会将stderr文本打印到屏幕上,而不是文件中(而且因为并没有stdout 的输出,所以out.txt没有内容):
$ ls + > out.txt
ls: cannot access +: No such file or directory
下面的命令中,我们将stderr重定向到out.txt:
$ ls + 2> out.txt #正常运行
可以将stderr单独重定向到一个文件,将stdout重定向到另一个文件:
$ cmd 2>stderr.txt 1>stdout.txt
还可以利用下面这个更好的方法将stderr转换成stdout,使得stderr和stdout 都被重定向到同一个文件中:
$ cmd 2>&1 output.txt
或者这样:
$ cmd &> output.txt
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(4)有时候,在输出中可能包含一些不必要的信息(比如调试信息)。如果你不想让终端 中充斥着有关stderr的繁枝末节,那么你可以将stderr的输出重定向到 /dev/null,
保证一切都会被清除得干干净净。假设我们有3个文件,分别是a1、a2、a3。但是普 通用户对文件a1没有“读 写 执行”权限。
如果需要打印文件名以a起始的所有文件 的内容,可以使用cat命令。设置一些测试文件:
$ echo a1 > a1
$ cp a1 a2 ; cp a2 a3;
$ chmod 000 a1 #清除所有权限
尽管可以使用通配符(a*)显示所有的文件内容,但是系统会显示一个出错信息, 因为对文件a1没有可读权限。
$ cat a*
cat: a1: Permission denied
a1
a1
其中,cat: a1: Permission denied属于stderr。我们可以将stderr信息重定向 到一个文件中,而stdout仍然保持不变。考虑如下代码:
$ cat a* 2> err.txt # stderr被重定向到err.txt a1
a1
$ cat err.txt
cat: a1: Permission denied
观察下面的代码:
$ cmd 2>/dev/null
当对如果对stderr或stdout进行重定向,被重定向的文本会传入文件。因为文本 已经被重定向到文件中,也就没剩下什么东西可以通过管道(|)传给接下来的命令, 而这些命令是通过stdin进行接收的。
(5)但是有一个方法既可以将数据重定向到文件,还可以提供一份重定向数据的副本作 为后续命令的stdin。这一切都可以使用tee来实现。举个例子:要在终端中打印 stdout,同时将它重定向到一个文件中,那么可以这样使用tee:
command | tee FILE1 FILE2
在下面的代码中,tee命令接收到来自stdin的数据。它将stdout的一份副本写入 文件out.txt,同时将另一份副本作为后续命令的stdin。命令cat -n将从stdin中接 收到的每一行数据前加上行号并写入stdout:
$ cat a* | tee out.txt | cat -n
cat: a1: Permission denied
1a1 2a1
查看out.txt的内容:
$ cat out.txt
a1
a1
注意,cat: a1: Permission denied 并没有在文件内容中出现。这是因为这些 信息属于stderr,而tee只能从stdin中读取。
默认情况下,tee命令会将文件覆盖,但它提供了一个-a选项,用于追加内容。例
如:$ cat a* | tee -a out.txt | cat –n。 带有参数的命令可以写成:command FILE1 FILE2...或者就简单的使用comman FILE。
(7)我们可以使用stdin作为命令参数。只需要将-作为命令的文件名参数即可:
$ cmd1 | cmd2 | cmd -
例如:
$ echo who is this | tee -
who is this
who is this
也可以将 /dev/stdin作为输出文件名来代替stdin。
类似地,使用 /dev/stderr代表标准错误,/dev/stdout代表标准输出。这些特殊的设备
文件分别对应stdin、stderr和stdout。
3. 自定义文件描述符
文件描述符是一种用于访问文件的抽象指示器(abstract indicator)。存取文件离不开被称为
“文件描述符”的特殊数字。0、1和2分别是stdin、stdout和stderr的预留描述符编号。 我们可以使用exec命令创建自己的文件描述符。如果你对用其他编程语言进行文件编程非
常熟悉,你可能已经注意到了文件打开模式。通常会用到3种模式。
只读模式。
截断写入模式。
追加写入模式。
< 操作符用于从文件中读取至stdin。> 操作符用于截断模式的文件写入(数据在目标文件 内容被截断之后写入)。>>操作符用于追加模式的文件写入。(数据被添加到文件的现有内容中, 而且该目标文件中原有的内容不会丢失。)文件描述符可以用以上三种模式中的任意一种来创建。
创建一个文件描述符进行文件读取:
$ exec 3<input.txt #使用文件描述符3打开并读取文件
我们可以这样使用它:
$ echo this is a test line > input.txt
$ exec 3<input.txt
现在你就可以在命令中使用文件描述符3了。例如: $ cat<&3
this is a test line
如果要再次读取,我们就不能继续使用文件描述符3了,而是需要用exec重新分配文件描述 符3来进行二次读取。
创建一个文件描述符用于写入(截断模式):
$ exec 4>output.txt #打开文件进行写入 例如:
$ exec 4>output.txt
$ echo newline >&4
$ cat output.txt
newline
创建一个文件描述符用于写入(追加模式):
$ exec 5>>input.txt
例如:
$ exec 5>>input.txt
$ echo appended line >&5
$ cat input.txt
newline