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

来源:互联网 发布:linux 终端关闭全屏 编辑:程序博客网 时间:2024/05/21 10:50

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/bashecho This is a error message! >&2echo 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.txtThis is a normal output$ cat test.txtThis is a error message!

8. 创建自己的重定向:

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

#!/bin/bash#exec命令可以使得对文件描述符的重定向在该脚本内永久有效(从使用exec的位置开始)#当然也可以再次使用exec对文件描述符重新分配exec 3> test.txt#创建了一个输出重定向echo This should display on the monitorecho and this should be stored in the file >&3echo Then this should be back on the monitorexec 3>&1#用另一个文件描述符来修改3的定向,称作重定向文件描述符,仍然是一个输出重定向echo This should display on the monitor >&3exec 4>> test.txt#重定向输出追加echo Appending part in the file >&4exec 5>&1#先临时保存1的值exec 1> info.txtecho In info.txtexec 1>&5#最后再还原echo Back on the monitor
$ bash test.shThis should display on the monitorThen this should be back on the monitorThis should display on the monitorBack on the monitor$ cat test.txtand this should be stored in the fileAppending part in the file$ cat info.txtIn info.txt
另一个小例子:

#!/bin/bashexec 2> err.infoecho 11111echo 22222exec 1> test.txtecho 3333echo 4444 >&2
$ bash test.shThis should display on the monitorThen this should be back on the monitorThis should display on the monitorBack on the monitor$ cat test.txtand this should be stored in the fileAppending part in the file$ cat info.txtIn info.txt
输入重定向的例子:

#!/bin/bashexec 6<&0#临时保存exec 0< read.txtcnt=1while read line#空行也会读哦!doecho "Line #$cnt: $line"cnt=$[ $cnt + 1 ]doneexec 0<&6#还原read -n1 -p "Are you done now [Y/N]? "echocase $REPLY inY | y) echo Goodbye!;;N | n) echo Sorry, this is in the end;;esac
一个等效的例子:

#!/bin/bashexec 3< read.txtcnt=1while read line <&3doecho "Line #$cnt: $line"cnt=$[ $cnt + 1 ]done

9. 关闭文件描述符:

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

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

#!/bin/bashexec 3> test.txtecho This is a test line >&3exec 3>&-echo "cat test.txt"cat test.txtecho Reopen itexec 3> test.txt#改成>>就是追加了echo Haha! >&3echo "cat test.txt again"cat test.txt

10. 有趣的读写重定向:

#!/bin/bashecho "cat test.txt"cat test.txtecho "processing..."exec 3<> test.txt   #定义一个读写描述符read line <&3echo This is a test line >&3echo "done."echo "cat test.txt again"cat test.txt
$ bash test.shcat test.txtThis is the first line.This is the second line.This is the third line.processing...done.cat test.txt againThis 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.txtcat info.txtwho | tee -a info.txt#-a表示追加,这样就不会覆盖了cat info.txt


0 0
原创粉丝点击