知识体系:
#回顾重定向
#标准输入和输出
#报告错误
#丢弃数据
#创建日志文件
这里主要讲述如何将脚本重定向到linux系统的各个位置。
1、了解输入和输出
我们知道显示脚本输出的方法有两种:
*在监视器屏幕上显示
*将输出重定向到文件
下面主要讲述如何使用标准的linux输入和输出体系满足将脚本输出定向到特定位置的需求。
1.1、标准文件描述符
linux使用文件描述符来标识每个文件对象,每个进程最多有9个可以打开文件的描述符(且非零),bash
shell为特殊需要保留了前三个文件描述符(0、1、2),如下所示:
**************************************
文件描述符
shell使用他们将shell中的默认输入输出重定向到相应的位置,默认就是监视器屏幕了。
下面简单讲述下这三个保留的文件描述符:
1》STDIN
STDIN文件描述符引用shell的标准输入,对于终端接口的标准输入就是键盘了。当然,我们可以使用输入重定向符号(<)也可以使其读取文件中的数据,跟键盘输入时一样的道理。先来看个例子:
[root@wzp ~]# cat
i am twentyfour , who are you ?
i am twentyfour , who are you ?
我直接输入cat后回车,结果我输入什么内容,系统就显示一样的内容。
这就是cat命令通过STDIN输入数据的示例。
[root@wzp
~]# vim testfile
[root@wzp ~]# cat < testfile
aaaaaaaaaaaa
bbbbbbbbbbbb
cccccccccccc
现在cat命令使用包含在testfile文件的中行作为输入,也是标准输入。
2》STDOUT
STDOUT文件描述符引用shell的标准输出,在终端接口的标准输出就是终端监视器。我们可以使用输出重定向(>)或者追加(>>)向文件添加数据。对于这个都是很好理解的,看个例子:
[root@wzp
~]# ls > 51cto.test
[root@wzp ~]# cat 51cto.test
51cto.test
anaconda-ks.cfg
Desktop
install.log
install.log.syslog
linux-linkto-mysql
login-mysql
mbox
sendto-mysql
如上把定向到监视器的内容重定向到文件中。
3》STDERR
STDERR文件描述符处理错误消息,默认情况下将所有错误消息显示在监视器上,具体应用下来讲述。
1.2、重定向错误
重定向STDERR数据的时候只要定义好STDERR文件描述符即可,这里头分两种情况的数据重定向。
(1)先来第一种:仅重定向错误消息
我们已经知道ETDERR文件描述符值为2,在重定向符号前附加2即可把错误消息重定向到具体的位置去,看个例子:
[root@wzp
~]# ll 51cto 2> errfile
[root@wzp ~]# cat errfile
ls: 51cto: 没有那个文件或目录
就是说明知道不存在51cto,然后去ll后把错误消息重定向到errfile中,而不是默认的显示在显示器上。下面的例子也如此:
[root@wzp
~]# ll 51cto.test 51cto 2>> errfile
-rw-r--r-- 1 root root 115 02-14 17:02 51cto.test
[root@wzp ~]# cat errfile
ls: 51cto: 没有那个文件或目录
ls: 51cto: 没有那个文件或目录
从结果都可以看出存在的文件显示在屏幕上,而不存在的文件的报错消息被追加到errfile文件中。
(2)还有一种是:重定向错误和数据
既然要分开处理错误消息和正确的数据显示,我们就可以通过1>符号进行标准输出重定向,和2>进行错误消息重定向,看个例子就明白了:
[root@wzp
~]# ls -al 51cto.test install.log linux rhca 1> exist 2> error
[root@wzp ~]# cat exist
-rw-r--r-- 1 root root
-rw-r--r-- 1 root root 27682 12-12 18:06 install.log
[root@wzp ~]# cat error
ls: linux: 没有那个文件或目录
ls: rhca: 没有那个文件或目录
当然,假如你想把正确输出的错误输出都重定向到一个文件的时候可以使用特殊的重定向符号:&>
先来看个例子:
[root@wzp
~]# ls -al 51cto.test install.log linux rhca &>
totalfile
[root@wzp ~]# cat totalfile
ls: linux: 没有那个文件或目录
ls: rhca: 没有那个文件或目录
-rw-r--r-- 1 root root
-rw-r--r-- 1 root root 27682 12-12 18:06 install.log
2、在脚本中重定向输出
使用脚本重定向输出的方法有两种:
*临时重定向每一行
*在脚本中永久性重定向所有命令
2.1、临时重定向
我们可以通过在文件描述符前加&符号来把一些输入重定向到文件描述符中,然后在重定向输入的时候调用这些文件描述符,也行听起来觉得怪怪的不好理解,OK,先来看个例子:
[root@wzp ~]# chmod u+x 5.2test
[root@wzp ~]# cat 5.2test
#!/bin/bash
echo "this is a error" >&2
echo "this is normal output"
[root@wzp ~]# ./5.2test
this is a error
this is normal output
这个脚本执行后没什么不同之处,但"this is a error" >&2已经是把这个"this is a
error"消息重定向给STDERR文件描述符了。
[root@wzp
~]# ./5.2test 2> errorfile
this is normal output
[root@wzp ~]# cat errorfile
this is a error
如上就表示把STDERR文件描述符的消息"this is a
error"重定向给errorfile文件。所以我们知道,这里头的动作有两个重定向!
2.2、永久重定向
如果在脚本中要重定向许多数据,可以通过exec命令通知shell在脚本执行中重定向特定的文件描述符,先看如下例子:
[root@wzp
~]# cat 5.2test
#!/bin/bash
exec 1>testfile
echo "welcome to CHINA"
date
[root@wzp ~]# ./5.2test
[root@wzp ~]# cat testfile
welcome to CHINA
2011年 02月 14日 星期一 19:15:25 CST
exec命令启动一个新的shell,并且STDOUT文件描述符重定向到testfile。
当然还可以在脚本中重定向到STDOUT,看如下例子:
[root@wzp
~]# cat 5.2test
#!/bin/bash
exec 2>testfile1
echo "aaaaaaaaa"
exec 1>testfile2
echo "bbbbbbbbb"
echo "ccccccccc" >&2
[root@wzp ~]# ./5.2test
aaaaaaaaa
[root@wzp ~]# cat testfile1
ccccccccc
[root@wzp ~]# cat testfile2
bbbbbbbbb
脚本内容是从上往下执行每个命令的,所以在STDERR后的echo
aa的内容势必显示在显示屏上。当定义了STDOUT后,bb的内容就被重定向到testfile2中。但是,虽然cc处于STDOUT之后,但是此行特意制定重定向到STDERR中,所以也就发生两次重定向最后处于testfile1中。
3、在脚本中重定向输入
重定向STDOUT和STDERR的技术也可以用来重定向键盘的STDIN,如
exec 0< testfile
该命令告诉shell应该从文件testfile而不是STDIN获取输入,如下例:
[root@wzp ~]# chmod u+x 5.3test
[root@wzp ~]# cat 5.3test
#!/bin/bash
exec 0< testfile
count=1
while read line
do
done
[root@wzp ~]# cat testfile
5555555
1111111
CCCCCCC
TTTTTTT
OOOOOOO
[root@wzp ~]# ./5.3test
Line #1: 5555555
Line #2: 1111111
Line #3: CCCCCCC
Line #4: TTTTTTT
Line #5: OOOOOOO
4、创建自己的重定向
一开始我就提及到shell中最多可以有9个打开的文件描述符,其他六个的编号是3~8,对于这6个文件描述符同样可运用在较本中。
4.1、创建输出文件描述符
直接来看一个例子,非常好理解:
[root@wzp ~]# chmod u+x 5.4test
[root@wzp ~]# cat 5.4test
#!/bin/bash
exec 3> testfile
echo "aaaaaaa"
echo "bbbbbbb" >&3
[root@wzp ~]# ./5.4test
aaaaaaa
[root@wzp ~]# cat testfile
bbbbbbb
对于aa的正常显示没啥好说,而bb的内容被重定向给文件描述符3,然后文件描述符3再次重定向给文件testfile,所以可以预知testfile的内容。
4.2、重定向文件描述符
对于重定向文件描述符说起来挺复杂的,先看个例子解释下:
[root@wzp
~]# cat 5.4test
#!/bin/bash
exec 3>&1
exec 1>testfile
echo "today is so cold"
exec 1>&3
echo "kobe bryant is great" >&3
[root@wzp ~]# ./5.4test
kobe bryant is great
[root@wzp ~]# cat testfile
today is so cold
第一次看如上的脚本,也许会头晕的。呵呵,把文件描述符3重定向给STDOUT,而STDOUT重定向给文件testfile,接下来的echo标准输出,所以对应上面的理念存到testfile中,先理解好这点!接下来就是STDOUT重定向给文件描述符3,而下面的echo重定向内容给文件描述符3,回头看脚本exec
3>&1,很明显把echo的内容重定向给etdout了,所以就显示在屏幕上
4.3、创建输入文件描述符
之所以要讲述这个就是说当出现有文件重定向输入文件描述符的时候,并且出现键盘输入这样的STDIN普通设置,我们可以通过先把默认的输入文件描述符重定向给2~9这样的文件描述符,等文件的内容被重定向结束后回过头来把先前设置的2~9文件描述符重定向到默认的STDIN,恢复原状。也许你对这段话看起来觉得费解,可以先理解下面的例子:
[root@wzp
~]# cat testfile
aaaaaaaaaaaa
bbbbbbbbbbbb
cccccccccccc
[root@wzp ~]# cat 5.4test
#!/bin/bash
exec 6<&0
exec 0< testfile
count=1
while read wzp
do
done
exec 0<&6
read -p "are you sure now ?"
case $REPLY in
Y | y) echo "OK , Goodbye";;
N | n) echo "Why ?";;
esac
[root@wzp ~]# ./5.4test
Line #1 : aaaaaaaaaaaa
Line #2 : bbbbbbbbbbbb
Line #3 : cccccccccccc
are you sure now ?y
OK , Goodbye
[root@wzp ~]# ./5.4test
Line #1 : aaaaaaaaaaaa
Line #2 : bbbbbbbbbbbb
Line #3 : cccccccccccc
are you sure now ?N
Why ?
4.4、关闭文件描述符
有时候想在脚本运行过程中关闭一些输入输出文件描述符,使得其失去存在意义,这个时候可以借用特殊符号 &-
exec 3>$-
#!/bin/bash
exec 8> testfile
echo "i will come back to school soon" >&8
exec 8>&-
echo "lalalalala" >&8
[root@wzp ~]# ./5.4test
./5.4test: line 5: 8: 错误的文件描述符
[root@wzp ~]# cat testfile
i will come back to school soon
把回校的消息重定向到&8,而&8重定向到testfile中,这点没什么质疑的。然后把&8关闭后,再次使用echo重定向给&8就报错了,当然,testfile的内容肯定是有的,脚本即便报错也是有运行结束。
5、列出开放文件描述符
因为可用的文件描述符有9个,所以有时候会出现很难跟踪哪个文件描述符重定向到哪个位置的情况发生,这个时候就可以借用losf命令理清线索。
lsof输出有很多参数和选项,常用的有-p,该参数可以指定进程ID;还有-d可以指定要显示饿文件描述符编号。
为了轻松确定进程当前的PID,可以借助特殊环境变量$$,而-a选择则是用于连接其他两个选项的结果,如下:
[root@wzp ~]# lsof -a -p $$ -d
0,1,2
COMMAND
bash
bash
bash
6、禁止命令输出
有时候一个脚本在后台运行出现了一些错误,shell就会通过mail通知进程的所有者,而这些错误却是无关紧要的。这个时候就可以利用回收站来处理这些错误信息。这就是我经常使用的linux系统特有的/dev/null空设置文件,可谓无底洞,给他多少吃多少。。。
先来看看例子:
[root@wzp ~]# echo aaaaa > /dev/null
[root@wzp ~]# cat /dev/null
查看的时候一直为空,再来看个例子:
[root@wzp
~]# cat testfile
i will come back to school soon
[root@wzp ~]# cat /dev/null > testfile
[root@wzp ~]# cat testfile
利用上面原本存在数据的testfile文件,把空文件重定向到该文件中,结果testfile的内容都被吃光了,O(∩_∩)O哈哈~
7、使用临时文件
linux系统有一个创建临时文件的特殊命令mktemp,可以在临时目录/tmp创建一个唯一的临时文件。不过,创建的临时文件不使用默认的umask值,而是600权限的文件。
7.1、创建本地临时文件
mktemp可以在任一目录创建临时文件,并且只需指定一个文件模板名即可。模板包括文本文件名和文件名后6的大写的X。如下:
[root@wzp ~]# mktemp 51cto.XXXXXX
51cto.Z26308
[root@wzp ~]# ll 51cto.Z26308
-rw------- 1 root root 0 02-14 23:05 51cto.Z26308
后面的Z26308是系统随意创建的,并且具有唯一性
[root@wzp ~]# mktemp 51cto.XXXXXX
51cto.n26366
[root@wzp ~]# mktemp 51cto.XXXXXX
51cto.o26367
[root@wzp ~]# mktemp 51cto.XXXXXX
51cto.G26369
[root@wzp ~]# ll 51cto.*
-rw------- 1 root root 0 02-14 23:07 51cto.G26369
-rw------- 1 root root 0 02-14 23:07 51cto.n26366
-rw------- 1 root root 0 02-14 23:07 51cto.o26367
所以放心,决不出现一样文件名的临时文件
7.2、在/tmp中创建临时文件
-t选项强迫mktemp在系统的/tmp目录下创建文件,这是一个挺不错的特性,直接看看例子:
[root@wzp
~]# mktemp -t wzp.XXXXXX
/tmp/wzp.I26701
[root@wzp ~]# ll /tmp/wzp.I26701
-rw------- 1 root root 0 02-14 23:16 /tmp/wzp.I26701
由于mktemp命令返回完整路径名,因此可以从linux系统引用临时文件,如下例子:
[root@wzp
~]# chmod u+x 7.2test
[root@wzp ~]# cat 7.2test
#!/bin/bash
tempfile=`mktemp -t tmp.XXXXXX`
echo "this is a temp file" >$tempfile
[root@wzp ~]# ll /tmp/tmp.u26849
-rw------- 1 root root 20 02-14 23:21 /tmp/tmp.u26849
[root@wzp ~]# cat /tmp/tmp.u26849
this is a temp file
通过这种方式非常的方便,灵活调用系统目录文件。
7.3、mktemp不仅可以创建临时文件,还可临时创建目录
以借用-d选项创建临时目录,下例:
[root@wzp
~]# chmod u+x 7.3test
[root@wzp ~]# ./7.3test
[root@wzp ~]# cat 7.3test
#!/bin/bash
tempdir=`mktemp -d dir.XXXXXX`
cd $tempdir
tempfile1=`mktemp temp.XXXXXX`
tempfile2=`mktemp temp.XXXXXX`
exec 7> $tempfile1
exec 8> $tempfile2
echo "this is 7" >&7
echo "this is 8" >&8
[root@wzp ~]# ll dir.A27050/
总计 16
-rw------- 1 root root 10 02-14 23:27 temp.f27052
-rw------- 1 root root 10 02-14 23:27 temp.p27051
[root@wzp ~]# cat dir.A27050/temp.f27052
this is 8
[root@wzp ~]# cat dir.A27050/temp.p27051
this is 7
通过创建临时目录后切换目录创建临时文件。
8、记录消息
有时候需要将输出同时发送到监视器和日志文件中,这个时候不需要使用两次重定向,只要使用特殊的tee命令即可。
比如看下简单的操作:
[root@wzp ~]# date | tee testfile
2011年 02月 14日 星期一 23:31:05 CST
[root@wzp ~]# cat testfile
2011年 02月 14日 星期一 23:31:05 CST
由于tee重定向来自于STDIN的数据,因此配合管道命令就可以把输出显示在监视屏和指定的文件中。
如果希望对如上的testfile文件不断增加这样的数据,必须使用-a选项,先看看一个效果:
[root@wzp
~]# cal | tee testfile
日 一 二 三 四 五 六
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28
[root@wzp ~]# cat testfile
日 一 二 三 四 五 六
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28
[root@wzp
~]# date | tee -a testfile
2011年 02月 14日 星期一 23:35:10 CST
[root@wzp ~]# cat testfile
日 一 二 三 四 五 六
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28
2011年 02月 14日 星期一 23:35:10 CST
对于这样的效果就比较好,可以把各个信息都归档到一个日志文件中。
本文出自 “twenty_four” 博客,请务必保留此出处http://twentyfour.blog.51cto.com/945260/521455