[bash]文件描述符、重定向以及tee命令

1. 文件描述符的概念:

Linux将所有内核对象当做文件来处理,系统用一个size_t类型来表示一个文件对象,比如对于文件描述符0就表示系统的标准输入设备STDIN,通常情况下STDIN的值为键盘,如read命令就默认从STDIN读取数据,当然STDIN的值是可以改变的,比如将其改成其他文件,这样的话想read等命令就会默认从相应的文件读取数据了。

简单地说,一个文件描述符可以和一个文件挂钩,一旦挂钩就可以通过取地址运算符&获得该文件的句柄,比如&0就可以获得STDIN设备在内存中的句柄(设备在系统中也被当做文件处理),可以这样理解,如果是一个shell中的普通变量var,可以通过$var的形式获得该变量所代表的值,而对于一个文件描述符fd,则可以通过&fd的形式获得文件描述符指向的文件的句柄,而这个句柄可以简单地理解成该文件的路径。

Linux中默认保留了三个固定的文件描述符0, 1, 2,分别表示STDIN、STDOUT、STDERR,分别表示标准输入设备,标准输出设备以及标准错误输出设备,它们的默认值为键盘、屏幕和屏幕,其具体值可以理解成这三个设备所代表的文件的文件路径,一些命令默认使用这三个设备进行输入输出,比如read就使用STDIN(0),echo就使用STDOUT(1),如果命令使用有误或者程序出错等的错误信息的输出就默认使用STDERR(3),也就是说默认将这些错误信息打印到终端,但是这三个文件描述符的值是可以随意改变的,即可以随意改变&0、&1和&2的值。

2. STDIN简介:

比如cat命令如果不带参数就可以默认从STDIN接收数据(即从键盘接收数据),如刚刚所说以下两个命令是等价的:

cat

cat <&0 #&0就是STDIN的值,就表示从STDIN处读取数据

当然也可以从其它地方读取文件,比如cat < test.txt,因此可以将&0看做文件描述符0所代表的文件的路径

3. STDOUT简介:

比如ls命令就是将结果默认输出到STDOUT设备中,因此以下两个命令是等价的:

ls

ls >&1 #&1就是STDOUT的值,可以理解成屏幕终端在系统中所代表的文件路径

当然也可以输出到其它地方,比如ls > test.txt

注意!对文件描述符fd取地址时必须和重定向符紧挨着,比如>$fd、<&fd,否则会报错!

4. 错误重定向与利用重定向符对文件描述符进行赋值:

我们形象地把文件描述符的值看做取地址后的值,比如文件描述符3的值就是&3,即该描述符所代表的文件的句柄(即文件的路径)。

对于错误的使用命令,比如查看一个不存在的文件:ls xxxx

运行完上述命令后错误信息会直接打印到终端上,因为STDERR(2)的值是终端,但是如果要将错误信息打印到其它地方的话就必须的修改STDERR的值。

修改的法则和Shell中对普通变量的赋值相类似:

var=test.txt,当然文件描述符不能直接这样赋值:2=test.txt,而是通过输出重定向符进行赋值:2> test.txt,其中fd必须和重定向符紧挨着否则会报错,但是重定向符后面可以带空格,但是这里的赋值有两层含义,第一层就是指赋值,第二层是表示与之挂钩的文件是输出性质的还是输入性质的,比如使用>就代表是输出性质的,表示test.txt文件将被作为一种输出文件使用,程序中的信息将输出到该文件,如果是<就表示是一种输入赋值,表示与之挂钩的文件将被当做输入设备,程序中的数据将从该文件中读入;

同样,也有变量之间的相互赋值:

var=$var1,而文件描述符之间也可以相互赋值,只不过不是这样的形式:3=$5,而是3>&5,这就表示将5所代表的文件的句柄赋值给3,即将和5挂钩的文件和3挂钩,同时使用3的时候将其作为输出设备使用,如果是<则表示将3作为输入设备使用。

这样,如果你想改变STDERR的值就可以这样使用:ls xxx 2> err.txt,这时错误信息就输出到了err.txt,但是这里对STDERR的修改只是临时的,只有对这条命令有效,这行完后STDERR又指向了终端屏幕了。

5. 同时重定向数据和错误信息的一个小例子:

ls -al test.txt xxxx 2> err.txt

此时只有xxxx的错误信息会输出到err.txt,而STDOUT(1)的值并未赋值,因此对于test.txt的正确信息仍然输出到了终端屏幕中。

同时重定向只要如下即可:

ls -al test.txt xxxx 1> ok.txt 2> err.txt,当然以下也是可以的

ls -al test.txt xxxx > ok.txt 2> err.txt,如果输出重定向左边不指定文件描述符则默认为1(STDOUT)

6. 将命令的所有输出到同一个文件中:

有一个特殊的文件描述符&,它就是两个输出描述符的合体,即& = 1 | 2,比如:

ls test.txt xxxx &> log.info,它和ls test.txt xxxx 1> log.info 2> log.info,&>有个特点就是输出的时候错误信息的优先级比数据的优先级要高,因此在输出文件中错误信息永远排在数据信息的前面,这样就方便在开头处查看错误信息(一般错误信息比较重要),而不用在整个文件中瞎找错误信息了)。

7. 一个临时重定向的简单例子:

#!/bin/bash

echo This is a error message! >&2
echo This is a normal output
$ bash test.sh	#由于命令中没有过给出重定向STDERR仍然为终端显示器
This is a error message!
This is a normal output
$ bash test.sh 2> test.txt	#STDERR被临时改成test.txt
This is a normal output
$ cat test.txt
This is a error message!

8. 创建自己的重定向:

在一个脚本中最多能使用9个描述符(0 ~ 8)

#!/bin/bash

#exec命令可以使得对文件描述符的重定向在该脚本内永久有效(从使用exec的位置开始)
#当然也可以再次使用exec对文件描述符重新分配
exec 3> test.txt	#创建了一个输出重定向

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

exec 3>&1	#用另一个文件描述符来修改3的定向,称作重定向文件描述符,仍然是一个输出重定向
echo This should display on the monitor >&3

exec 4>> test.txt	#重定向输出追加
echo Appending part in the file >&4

exec 5>&1	#先临时保存1的值
exec 1> info.txt
echo In info.txt
exec 1>&5	#最后再还原
echo Back on the monitor
$ bash test.sh
This should display on the monitor
Then this should be back on the monitor
This should display on the monitor
Back on the monitor
$ cat test.txt
and this should be stored in the file
Appending part in the file
$ cat info.txt
In info.txt
另一个小例子:

#!/bin/bash

exec 2> err.info

echo 11111
echo 22222

exec 1> test.txt
echo 3333
echo 4444 >&2
$ bash test.sh
This should display on the monitor
Then this should be back on the monitor
This should display on the monitor
Back on the monitor
$ cat test.txt
and this should be stored in the file
Appending part in the file
$ cat info.txt
In info.txt
输入重定向的例子:

#!/bin/bash

exec 6<&0	#临时保存
exec 0< read.txt

cnt=1
while read line#空行也会读哦!
do
	echo "Line #$cnt: $line"
	cnt=$[ $cnt + 1 ]
done

exec 0<&6	#还原
read -n1 -p "Are you done now [Y/N]? "
echo
case $REPLY in
	Y | y) echo Goodbye!;;
	N | n) echo Sorry, this is in the end;;
esac
一个等效的例子:

#!/bin/bash

exec 3< read.txt

cnt=1
while read line <&3
do
	echo "Line #$cnt: $line"
	cnt=$[ $cnt + 1 ]
done

9. 关闭文件描述符:

只要给相应的文件描述符赋一个空指针NULL就行了,但是文件描述符没有NULL一说,但是有一个特殊的文件描述符-表示NULL,所以只要exec fd>&-即可,或者exec fd<&-也行,前者是关闭了一个输出重定向后者则关闭了一个输入重定向,一旦关闭后在尝试使用则会报错。

#!/bin/bash

exec 3> test.txt
echo This is a test line >&3

exec 3>&-
echo "This won't work" >&3
$ bash test.sh
test.sh: line 7: 3: Bad file descriptor
对于输出重定向,如果关闭后在打开,则输出会覆盖:

#!/bin/bash

exec 3> test.txt
echo This is a test line >&3
exec 3>&-

echo "cat test.txt"
cat test.txt

echo Reopen it
exec 3> test.txt	#改成>>就是追加了
echo Haha! >&3

echo "cat test.txt again"
cat test.txt

10. 有趣的读写重定向:

#!/bin/bash

echo "cat test.txt"
cat test.txt

echo "processing..."
exec 3<> test.txt   #定义一个读写描述符
read line <&3
echo This is a test line >&3
echo "done."

echo "cat test.txt again"
cat test.txt
$ bash test.sh
cat test.txt
This is the first line.
This is the second line.
This is the third line.
processing...
done.
cat test.txt again
This is the first line.
This is a test line	#这很有趣,因为对于读写重定向,两种操作共同维护统一指针,读一段后指针会偏移相应的一段距离,写的时候会接着上一次读后指针所在的位置继续往下写,因此会覆盖一部分原来的文本信息
ine.
This is the third line.

11. 将输出引向黑洞以阻止输出:

黑洞文件是一种特殊的设备:/dev/null,输出到该文件中的任何信息都会丢失,从该文件中读取的信息则是空,就如它的名字null一样

比如ls -l xxx 2> /dev/null,将不显示任何错误信息

cat /dev/null > test.txt,清空一个文件而不需要繁琐地先rm再touchu两步了


12. 使用tee命令让信息即显示在终端也保存在记录文件中:

#!/bin/bash

#tee其实是一个T型的管道接头,一端导入终端屏幕,另一端则是自己定义
#这样就到达了双重效果
date | tee info.txt
cat info.txt

who | tee -a info.txt	#-a表示追加,这样就不会覆盖了
cat info.txt


展开阅读全文

没有更多推荐了,返回首页