首页 > 代码库 > Linux Shell脚本编写——呈现数据(五)

Linux Shell脚本编写——呈现数据(五)

Linux系统将每个对象当做文件来处理。这包括输入和输出的过程。Linux用文件描述符来标识每个文件对象。文件描述符是一个非负整数,可以唯一地标识会话中打开的文件。每个过程一次最多可以有9个文件描述符。出于特殊目的,bash shell保留了最早的3个文件描述符(0、1和2) 

Linux的标准文件描述符
文件描述符 缩写 描述
0 STDIN 标准输入
1 STDOUT 标准输出
2 SRDERR 标准错误

STDIN

STDIN文件描述代表shell的标准输入。对于终端界面来说,标准输入就是键盘,shell从SDTIN文件描述符对应的键盘获得输入,在用户输入时处理每个字符,在使用输入重定向符号(<)时,Linux会用重定向指定的文件来替换标准输入文件的描述符。它会读取文件并提取数据,就如同它是从键盘上键入的

只输入cat命令时,它会接受STDIN输入,当用户在每行输入时,cat命令会将每行显示在输出,按Ctrl+C退出

代码5-1

root@lejian:~# 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

root@lejian:~# cat text 
This is the first line
This is the second line
This is the third line
root@lejian:~# cat < text 
This is the first line
This is the second line
This is the third line

  

STDOUT 

STDOUT文件描述代表标准的shell输出。在终端界面上,标准输出就是终端显示器。shell的所有输出会被定向到标准输出中

代码5-3

root@lejian:~# ls -l /home/ > demo1
root@lejian:~# 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

root@lejian:~# ls -l /home/software/ >> demo1 
root@lejian:~# 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

root@lejian:~# ls -l /badfile/ > demo2
ls: cannot access /badfile/: No such file or directory
root@lejian:~# cat demo2 
root@lejian:~# 

  

STDERR

shell通过特殊的STDERR文件描述符来处理错误消息。shell或shell中运行的程序和脚本出错时生成的错误消息都会发送到这个位置将标准错误符号写在重定向符号的前面,可将错误信息打印进error文件,但只能打印错误信息

代码5-6

root@lejian:~# ls -l /badfile/ 2> error
root@lejian:~# cat error 
ls: cannot access /badfile/: No such file or directory

  

重定向错误和数据

代码5-7

root@lejian:~# ls -l
total 4
-rw-r--r-- 1 root root 800 Dec  4 20:41 demo1
root@lejian:~# ls -al demo1 demo2 demo3 demo4 demo5 demo6 2>error 1>text
root@lejian:~# ls
demo1  error  text
root@lejian:~# 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
root@lejian:~# cat text 
-rw-r--r-- 1 root root 800 Dec  4 20:41 demo1

  

可以将STDERR和STDOUT的输出重定向到同一个输出文件

代码5-8

root@lejian:~# ls -l
total 4
-rw-r--r-- 1 root root 800 Dec  4 20:41 demo1
root@lejian:~# ls -al demo1 demo2 demo3 demo4 demo5 demo6 &>text
root@lejian:~# 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

root@lejian:~# cat demo2 
#!/bin/bash
echo "This is an error" >&2 
echo "This is normal output"
root@lejian:~# ./demo2 
This is an error
This is normal output
root@lejian:~# ./demo2 2>error
This is normal output
root@lejian:~# cat error 
This is an error

  

在脚本中重定向输出

代码5-10

root@lejian:~# cat demo3 
#!/bin/bash
exec 2>error
echo "Hello world"
exec 1>out
echo "Hello Spring">&2
echo "Hello MyBatis"
root@lejian:~# ./demo3 
Hello world
root@lejian:~# cat out 
Hello MyBatis
root@lejian:~# cat error 
Hello Spring

  

exec 0<text,这个命令告诉shell从text文件中获取输入而不是STDIN

代码5-11

root@lejian:~# cat text 
Hello Spring
Hello Hibernate
Hello MyBatis
root@lejian:~# cat demo4 
#!/bin/bash
exec 0<text
count=1
while read line
do
        echo "Line:#$count:$line"
        ((count++))
done
root@lejian:~# ./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

root@lejian:~# 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"
root@lejian:~# ./demo5 
Hello Spring
Hello MyBatis
Hello Shiro
root@lejian:~# cat out 
Hello World
Hello Hibernate

  

代码5-13中, 文件描述符6用来保存STDIN的位置,然后脚本将STDIN重定向到一个文件。read命令的所有输入都是从重定向后的STDIN中来的,也就是输入文件。在读取完所有行后,脚本会将STDIN重定向到文件描述符6,从而将STDIN恢复到原先的位置

代码5-13

root@lejian:/data# cat text 
Spring
MyBatis
Hibernate
Shiro
Hadoop
Spark
root@lejian:/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
root@lejian:/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

root@lejian:/data# cat text 
Spring
MyBatis
Hibernate
Shiro
Hadoop
Spark
root@lejian:/data# cat demo3 
#!/bin/bash
exec 3<>text
read line<&3
echo "Read:$line"
echo "This is a test line">&3
root@lejian:/data# ./demo3 
Read:Spring
root@lejian:/data# cat text 
Spring
This is a test line
iro
Hadoop
Spark

  

当创建新的输入或输出文件描述符,Linux会在脚本退出时自动关闭它们,要关闭文件描述符,将它重定向到特殊符号&-

exec 3>&-

代码5-15

root@lejian:/data# cat demo4 
#!/bin/bash
exec 3>text
echo "Hello Spring">&3
exec 3>&-
echo "Hello Hibernate" >&3
root@lejian:/data# ./demo4 
./demo4: line 5: 3: Bad file descriptor
root@lejian:/data# cat text 
Hello Spring

  

如代码5-15,一旦关闭了文件描述符,就不能在脚本中向它写入任何数据了,否则shell会生成错误消息

另外需要注意的一点是,关闭文件描述符后,如果后面又打开同一个输出文件,shell会用一个新文件替换已有文件这意味着之前的数据不会存在

代码5-16

root@lejian:/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
root@lejian:/data# ./demo5 
Hello World
root@lejian:/data# cat text 
Hello Spring
Hello Hibernate

  

如果不希望在运行程序时又错误消息出现,可以将STDERR重定向到/dev下一个称为null的特殊文件,shell输出到null文件的任何数据都不会保存

代码5-17

root@lejian:/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
root@lejian:/data# ls -al /home/ > /dev/null 
root@lejian:/data# cat /dev/null 
root@lejian:/data# 

  

如果将/dev/null作为输入文件,将会清空现有文件数据,可用它来清除数据而不用删除文件

代码5-18

root@lejian:/data# cat text 
Hello Spring
Hello Hibernate
root@lejian:/data# cat /dev/null > text 
root@lejian:/data# cat text 
root@lejian:/data# 

  

 创建本地临时文件,点可加可不加,末尾至少加3个X,且只能加X(必须大写)

代码5-19

root@lejian:/data# mktemp test.XXX
test.otX
root@lejian:/data# mktemp test.XXXXX
test.gYDV4
root@lejian:/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

root@lejian:/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
root@lejian:/data# ./demo6 
Hello Java
test.heV content:
Hello Spring
Hello Hibernate
Hello MyBatis
Hello Shiro
Hello Drools
Hello Activity

  

mktemp命令使用-t参数返回全路径

代码5-21

root@lejian:/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
root@lejian:/data# rm /tmp/test*
root@lejian:/data# mktemp -t test.XXX
/tmp/test.ZUV
root@lejian:/data# mktemp -t test.XXXXX
/tmp/test.JcyNo
root@lejian:/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

root@lejian:/data# mktemp -d test.XXX
test.U31
root@lejian:/data# mktemp -d test.XXXXX
test.UZBuc
root@lejian:/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
root@lejian:/data# mktemp -dt test.XXXXX
/tmp/test.Wl6oP
root@lejian:/data# mktemp -dt test.XXXXXXXX
/tmp/test.biKCpQ7C
root@lejian:/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

root@lejian:/data# date | tee text
Mon Dec  5 06:18:04 CST 2016
root@lejian:/data# cat text 
Mon Dec  5 06:18:04 CST 2016
root@lejian:/data# who | tee text 
root     pts/0        2016-12-05 04:26 (122.90.143.21)
root@lejian:/data# cat text 
root     pts/0        2016-12-05 04:26 (122.90.143.21)
root@lejian:/data# date | tee -a text 
Mon Dec  5 06:18:29 CST 2016
root@lejian:/data# cat text 
root     pts/0        2016-12-05 04:26 (122.90.143.21)
Mon Dec  5 06:18:29 CST 2016

  

Linux Shell脚本编写——呈现数据(五)