Linux系统将每个对象当做文件来处理。这包括输入和输出的过程。Linux用文件描述符来标识每个文件对象。文件描述符是一个非负整数,可以唯一地标识会话中打开的文件。每个过程一次最多可以有9个文件描述符。出于特殊目的,bash shell保留了最早的3个文件描述符(0、1和2)
文件描述符 | 缩写 | 描述 |
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | SRDERR | 标准错误 |
STDIN
STDIN文件描述代表shell的标准输入。对于终端界面来说,标准输入就是键盘,shell从SDTIN文件描述符对应的键盘获得输入,在用户输入时处理每个字符,在使用输入重定向符号(<)时,Linux会用重定向指定的文件来替换标准输入文件的描述符。它会读取文件并提取数据,就如同它是从键盘上键入的
只输入cat命令时,它会接受STDIN输入,当用户在每行输入时,cat命令会将每行显示在输出,按Ctrl+C退出
代码5-1
[email protected]:~# cat this is a test this is a test this is a second test this is a second test ^C
通过STDIN重定向符号强制cat命令接受来自另一个非STDIN文件的输入
代码5-2
[email protected]:~# cat text This is the first line This is the second line This is the third line [email protected]:~# cat < text This is the first line This is the second line This is the third line
STDOUT
STDOUT文件描述代表标准的shell输出。在终端界面上,标准输出就是终端显示器。shell的所有输出会被定向到标准输出中
代码5-3
[email protected]:~# ls -l /home/ > demo1 [email protected]:~# cat demo1 total 36 drwxr-xr-x 58 root root 4096 Nov 28 16:52 backup drwxr-xr-x 2 root root 4096 Aug 25 10:29 conf drwx------ 2 root root 16384 Aug 24 16:53 lost+found drwxr-xr-x 4 root root 4096 Aug 25 10:29 mysql drwxr-xr-x 8 root root 4096 Oct 8 13:09 software drwxr-xr-x 2 root root 4096 Aug 25 10:41 tmp
代码5-4
[email protected]:~# ls -l /home/software/ >> demo1 [email protected]:~# cat demo1 total 36 drwxr-xr-x 58 root root 4096 Nov 28 16:52 backup drwxr-xr-x 2 root root 4096 Aug 25 10:29 conf drwx------ 2 root root 16384 Aug 24 16:53 lost+found drwxr-xr-x 4 root root 4096 Aug 25 10:29 mysql drwxr-xr-x 8 root root 4096 Oct 8 13:09 software drwxr-xr-x 2 root root 4096 Aug 25 10:41 tmp total 233600 drwxr-xr-x 9 root root 4096 Nov 25 2014 apache-tomcat-1 drwxr-xr-x 9 root root 4096 Nov 25 2014 apache-tomcat-2 drwxr-xr-x 9 root root 4096 Aug 22 08:32 apache-tomcat-rental drwxr-xr-x 3 root root 4096 Jan 8 2015 code drwxr-xr-x 8 uucp 143 4096 Nov 25 2014 java -rw-r--r-- 1 root root 55062846 Aug 25 10:41 rental.tar.gz -rw-r--r-- 1 root root 30531249 Oct 8 10:14 scala-2.10.3.tgz -rw-r--r-- 1 root root 153586658 Aug 25 10:34 software.tar.gz
当命令生成错误消息时,shell并未将错误消息重定向到输出重定向文件。shell创建了输出重定向文件,但错误消息却显示在显示器上
代码5-5
[email protected]:~# ls -l /badfile/ > demo2 ls: cannot access /badfile/: No such file or directory [email protected]:~# cat demo2 [email protected]:~#
STDERR
shell通过特殊的STDERR文件描述符来处理错误消息。shell或shell中运行的程序和脚本出错时生成的错误消息都会发送到这个位置将标准错误符号写在重定向符号的前面,可将错误信息打印进error文件,但只能打印错误信息
代码5-6
[email protected]:~# ls -l /badfile/ 2> error [email protected]:~# cat error ls: cannot access /badfile/: No such file or directory
重定向错误和数据
代码5-7
[email protected]:~# ls -l total 4 -rw-r--r-- 1 root root 800 Dec 4 20:41 demo1 [email protected]:~# ls -al demo1 demo2 demo3 demo4 demo5 demo6 2>error 1>text [email protected]:~# ls demo1 error text [email protected]:~# cat error ls: cannot access demo2: No such file or directory ls: cannot access demo3: No such file or directory ls: cannot access demo4: No such file or directory ls: cannot access demo5: No such file or directory ls: cannot access demo6: No such file or directory [email protected]:~# cat text -rw-r--r-- 1 root root 800 Dec 4 20:41 demo1
可以将STDERR和STDOUT的输出重定向到同一个输出文件
代码5-8
[email protected]:~# ls -l total 4 -rw-r--r-- 1 root root 800 Dec 4 20:41 demo1 [email protected]:~# ls -al demo1 demo2 demo3 demo4 demo5 demo6 &>text [email protected]:~# cat text ls: cannot access demo2: No such file or directory ls: cannot access demo3: No such file or directory ls: cannot access demo4: No such file or directory ls: cannot access demo5: No such file or directory ls: cannot access demo6: No such file or directory -rw-r--r-- 1 root root 800 Dec 4 20:41 demo1
如果故意要在脚本中生成错误消息,可以将单独的一行输出重定向STDERR,默认情况下,Linux会将STDERR定向到STDOUT,但是如果运行脚本时重定向了STDERR,脚本中所有定向到STDERR的文本会被重定向
代码5-9
[email protected]:~# cat demo2 #!/bin/bash echo "This is an error" >&2 echo "This is normal output" [email protected]:~# ./demo2 This is an error This is normal output [email protected]:~# ./demo2 2>error This is normal output [email protected]:~# cat error This is an error
在脚本中重定向输出
代码5-10
[email protected]:~# cat demo3 #!/bin/bash exec 2>error echo "Hello world" exec 1>out echo "Hello Spring">&2 echo "Hello MyBatis" [email protected]:~# ./demo3 Hello world [email protected]:~# cat out Hello MyBatis [email protected]:~# cat error Hello Spring
exec 0<text,这个命令告诉shell从text文件中获取输入而不是STDIN
代码5-11
[email protected]:~# cat text Hello Spring Hello Hibernate Hello MyBatis [email protected]:~# cat demo4 #!/bin/bash exec 0<text count=1 while read line do echo "Line:#$count:$line" ((count++)) done [email protected]:~# ./demo4 Line:#1:Hello Spring Line:#2:Hello Hibernate Line:#3:Hello MyBatis
Linux并不局限3个默认的文件描述符,shell中最多可以有9个打开的文件描述符,其他文件描述符从3排到8,其他描述符与3个默认的文件描述符用法类似,exec 3>>testfile会把输出追加到一个新的文件
下面这个例子:首先,脚本将文件描述符3重定向到文件描述符1的当前位置,也就是STDOUT,意味着发送给文件描述符3都会显示在屏幕上
在向STDOUT发送一些输出后,脚本会将STDOUT重定向到文件描述符3的当前位置,也就是显示器,即它原先的位置
代码5-12
[email protected]:~# cat demo5 #!/bin/bash exec 3>&1 exec 1>out echo "Hello World" echo "Hello Spring">&3 echo "Hello Hibernate" exec 1>&3 echo "Hello MyBatis">&3 echo "Hello Shiro" [email protected]:~# ./demo5 Hello Spring Hello MyBatis Hello Shiro [email protected]:~# cat out Hello World Hello Hibernate
代码5-13中, 文件描述符6用来保存STDIN的位置,然后脚本将STDIN重定向到一个文件。read命令的所有输入都是从重定向后的STDIN中来的,也就是输入文件。在读取完所有行后,脚本会将STDIN重定向到文件描述符6,从而将STDIN恢复到原先的位置
代码5-13
[email protected]:/data# cat text Spring MyBatis Hibernate Shiro Hadoop Spark [email protected]:/data# cat demo2 #!/bin/bash exec 6<&0 exec 0<text count=1 while read line do echo "Line #count:$line" ((count++)) done exec 0<&6 read -p "Are you learned now?" answer case $answer in Y|y) echo "Good job";; N|n) echo "GoodBye";; esac [email protected]:/data# ./demo2 Line #count:Spring Line #count:MyBatis Line #count:Hibernate Line #count:Shiro Line #count:Hadoop Line #count:Spark Are you learned now?n GoodBye
也可以打开单个文件描述符来作为输入和输出,可以从同一个文件中读取数据,再将数据写入到同一个文件中,下例:exec命令将用作读取输入和写入文件描述符3分配给文件text。下一步,它通过分配好的文件描述符来用read命令读取文件中的第一行,然后将读取的那行输入显示在STDOUT上。之后,它用echo语句来向文件描述符打开的文件写入一行数据
当脚本向文件写入数据时,它会从文件指针所处的位置开始。read命令读取了第一行数据。所以它会让文件指针指向第二行数据的第一个字符。在echo语句将数据输出到文件中,它会将数据放在文件指针的当前位置,覆盖该位置的数据
代码5-14
[email protected]:/data# cat text Spring MyBatis Hibernate Shiro Hadoop Spark [email protected]:/data# cat demo3 #!/bin/bash exec 3<>text read line<&3 echo "Read:$line" echo "This is a test line">&3 [email protected]:/data# ./demo3 Read:Spring [email protected]:/data# cat text Spring This is a test line iro Hadoop Spark
当创建新的输入或输出文件描述符,Linux会在脚本退出时自动关闭它们,要关闭文件描述符,将它重定向到特殊符号&-
exec 3>&-
代码5-15
[email protected]:/data# cat demo4 #!/bin/bash exec 3>text echo "Hello Spring">&3 exec 3>&- echo "Hello Hibernate" >&3 [email protected]:/data# ./demo4 ./demo4: line 5: 3: Bad file descriptor [email protected]:/data# cat text Hello Spring
如代码5-15,一旦关闭了文件描述符,就不能在脚本中向它写入任何数据了,否则shell会生成错误消息
另外需要注意的一点是,关闭文件描述符后,如果后面又打开同一个输出文件,shell会用一个新文件替换已有文件这意味着之前的数据不会存在
代码5-16
[email protected]:/data# cat demo5 #!/bin/bash exec 3>text echo "Hello World">&3 exec 3>&- cat text exec 3>text echo "Hello Spring">&3 echo "Hello Hibernate">&3 [email protected]:/data# ./demo5 Hello World [email protected]:/data# cat text Hello Spring Hello Hibernate
如果不希望在运行程序时又错误消息出现,可以将STDERR重定向到/dev下一个称为null的特殊文件,shell输出到null文件的任何数据都不会保存
代码5-17
[email protected]:/data# ls -al /home/ total 44 drwxr-xr-x 8 root root 4096 Aug 25 10:29 . drwxr-xr-x 28 root root 4096 Nov 11 20:08 .. drwxr-xr-x 58 root root 4096 Nov 28 16:52 backup drwxr-xr-x 2 root root 4096 Aug 25 10:29 conf drwx------ 2 root root 16384 Aug 24 16:53 lost+found drwxr-xr-x 4 root root 4096 Aug 25 10:29 mysql drwxr-xr-x 8 root root 4096 Oct 8 13:09 software drwxr-xr-x 2 root root 4096 Aug 25 10:41 tmp [email protected]:/data# ls -al /home/ > /dev/null [email protected]:/data# cat /dev/null [email protected]:/data#
如果将/dev/null作为输入文件,将会清空现有文件数据,可用它来清除数据而不用删除文件
代码5-18
[email protected]:/data# cat text Hello Spring Hello Hibernate [email protected]:/data# cat /dev/null > text [email protected]:/data# cat text [email protected]:/data#
创建本地临时文件,点可加可不加,末尾至少加3个X,且只能加X(必须大写)
代码5-19
[email protected]:/data# mktemp test.XXX test.otX [email protected]:/data# mktemp test.XXXXX test.gYDV4 [email protected]:/data# ls -l total 0 -rw------- 1 root root 0 Dec 5 05:29 test.gYDV4 -rw------- 1 root root 0 Dec 5 05:29 test.otX
脚本中使用mktemp
代码5-20
[email protected]:/data# cat demo6 #!/bin/bash tempfile=`mktemp test.XXX` exec 3>$tempfile echo "Hello Java" echo "Hello Spring">&3 echo "Hello Hibernate">&3 echo "Hello MyBatis">&3 echo "Hello Shiro">&3 echo "Hello Drools">&3 echo "Hello Activity">&3 exec 3>&- echo "$tempfile content:" cat $tempfile rm -rf $tempfile [email protected]:/data# ./demo6 Hello Java test.heV content: Hello Spring Hello Hibernate Hello MyBatis Hello Shiro Hello Drools Hello Activity
mktemp命令使用-t参数返回全路径
代码5-21
[email protected]:/data# ls -l /tmp/test* -rw------- 1 root root 0 Dec 5 05:43 /tmp/test.4WExx -rw------- 1 root root 0 Sep 30 20:43 /tmp/testing.t90 -rw------- 1 root root 0 Sep 30 20:43 /tmp/testingW85 -rw------- 1 root root 0 Dec 5 05:43 /tmp/test.JxQ [email protected]:/data# rm /tmp/test* [email protected]:/data# mktemp -t test.XXX /tmp/test.ZUV [email protected]:/data# mktemp -t test.XXXXX /tmp/test.JcyNo [email protected]:/data# ls -l /tmp/test* -rw------- 1 root root 0 Dec 5 05:44 /tmp/test.JcyNo -rw------- 1 root root 0 Dec 5 05:44 /tmp/test.ZUV
使用-d创建临时目录,加上-t同时也可以返回创建临时目录的路径
代码5-22
[email protected]:/data# mktemp -d test.XXX test.U31 [email protected]:/data# mktemp -d test.XXXXX test.UZBuc [email protected]:/data# ls -l total 8 drwx------ 2 root root 4096 Dec 5 05:46 test.U31 drwx------ 2 root root 4096 Dec 5 05:46 test.UZBuc [email protected]:/data# mktemp -dt test.XXXXX /tmp/test.Wl6oP [email protected]:/data# mktemp -dt test.XXXXXXXX /tmp/test.biKCpQ7C [email protected]:/data# ls -l /tmp/test* -rw------- 1 root root 0 Dec 5 05:44 /tmp/test.JcyNo -rw------- 1 root root 0 Dec 5 05:44 /tmp/test.ZUV /tmp/test.biKCpQ7C: total 0 /tmp/test.Wl6oP: total 0
tee可以一边向显示器输出,一边重定向文件,但一般是覆盖原文件内容,可使用-a追加内容
代码5-23
[email protected]:/data# date | tee text Mon Dec 5 06:18:04 CST 2016 [email protected]:/data# cat text Mon Dec 5 06:18:04 CST 2016 [email protected]:/data# who | tee text root pts/0 2016-12-05 04:26 (122.90.143.21) root[email protected]:/data# cat text root pts/0 2016-12-05 04:26 (122.90.143.21) [email protected]:/data# date | tee -a text Mon Dec 5 06:18:29 CST 2016 [email protected]:/data# cat text root pts/0 2016-12-05 04:26 (122.90.143.21) Mon Dec 5 06:18:29 CST 2016