Key words
了解输入和输出      标准文件描述      在脚本中重定向输入和输出           创建自己的重定向      命令tee
 
一、了解输入和输出
我们常见的显示输出方式有两种:
a、直接在屏幕上显示输出    b 、将输出重定向到文件中
这两种方法要么生成全部数据输出,要么没有任何输出。但有时候需要直接在显示屏上显示一部分数据,在文件中显示另一部分数据。在这种情况下,最好能了解
Linux
处理输入输出的方式,一遍让脚本输出到正确的位置。
  
标准文件描述
        Linux
系统将每个对象(包括输入和输出)当作文件处理,并使用文件描述符标识每个文件对象。文件描述符
是一个非负整数,可以唯一地标识会话中打开的文件。每个进程最多可以有
9
个打开文件的描述符。
      bash shell 为特殊需要保留了前三个文件描述符:
0
1
2 分别代
表标准输入、标准输出、标准错误
1
0
  
:
 
STDIN
标准输入
       
对于终端接口,标准输入是键盘,
shell
通过
STDIN
文件描述符从键盘接受输入。
      
除了从键盘输入字符,我们还可以使用输入重定向符号
 
<
)来获取数据,就像通过键盘输入一样。
[root@localhost ~]# cat test
/root/bb
[root@localhost ~]# cat < test
/root/bb
[root@localhost ~]# ll < test
总计 1548
-rw-r--r-- 1 root root    205 07-24 21:04 \
-rwxr--r-- 1 root root     22 07-29 09:24 aa
-rw------- 1 root root   1177 04-09 05:58 anaconda-ks.cfg
drwxr-xr-x 2 root root   4096 07-29 21:30 bb
-rw-r--r-- 1 root root   7336 07-25 05:22 count
-rwxr--r-- 1 root root     65 07-25 22:35 delusers
drwxr-xr-x 4 root root   4096 07-29 00:13 Desktop
。。。。。。
注意:
<
只是将
test
这个文件名重定向给前面的命令,而没有重定向文件里面的内容,所以
ll
命令显示的只是当前目录下的内容而没有显示
/root/bb
目录下的内容。
 
2、1 : SDOUT 标准输出
      
在终端接口,标准输出是显示器屏幕。当然,可以利用从定向符号
>
(
覆盖重定向
)
>>
(
追加重定向
)
到指定的文件。错误信息除外。
例:覆盖和追加的分别
[root@localhost ~]# cat test
wlt
[root@localhost ~]# echo wzp > test
[root@localhost ~]# cat test
wzp
[root@localhost ~]# echo wlt >> test
[root@localhost ~]# cat test
wzp
wlt
 
3、 2 :STDERR 标准错误
 
 STDERR
文件描述符和
STDOUT
文件描述符指向相同的位置,这意味着默认情况下,所有错误信息都显示在显示器上,处理脚本时通常要更改该行为,尤其是需要在日志文件中记录错误消息时。
例:利用文件描述符
2
重定向错误
[root@localhost ~]# ls -al notexitfile 2> test
[root@localhost ~]# cat test
ls: notexitfile: 没有那个文件或目录
如果需要同时重定向错误和正常数据,则必须要使用两个重定向符号:
[root@localhost ~]# ls -al  notexitfile /tm p
2>  errorfile
 1>  normalfile
[root@localhost ~]# cat errorfile
ls: notexitfile: 没有那个文件或目录
[root@localhost ~]# cat normalfile
/tmp:
总计 672
drwxrwxrwt 61 root root 4096 07-29 21:22 .
drwxr-xr-x 29 root root 4096 07-29 21:18 ..
drwxrwxrwt 2 root root 4096 07-29 21:18 .font-unix
drwx------ 3 root root 4096 07-21 20:31 gconfd-root
-rw------- 1 root root   66 07-19 22:22 .gdm0R2QYV
 
。。。。。。
还可以把所有的信息都放在一个文件里面: &>
[root@localhost ~]# ls -al notexitfile /tmp
 &> test
[root@localhost ~]# cat test
ls: notexitfile: 没有那个文件或目录
/tmp:
总计 672
drwxrwxrwt 61 root root 4096 07-29 21:22 .
drwxr-xr-x 29 root root 4096 07-29 21:18 ..
drwxrwxrwt 2 root root 4096 07-29 21:18 .font-unix
drwx------ 3 root root 4096 07-21 20:31 gconfd-root
。。。。。。
 
二、在脚本中重定向输入和输出
1、
在脚本中重定向输入
使用命令
exec :exec 0< testfile
这个命令告诉
shell
从文件
testfile
而不是
STDIN
获取输入。
例:
[root@localhost ~]# cat testfile
 first line
 second line
 third line
[root@localhost ~]# cat test
#!/bin/bash
exec 0< testfile
count=1
while read line
do
echo "Line $count : $line"
 count=$[ $count + 1 ]
done
 
[root@localhost ~]# ./test
Line 1 : first line
Line 2 : second line
Line 3 : third line
[root@localhost ~]#
 
2、使用脚本重定向输出
方式有两种:
a
、临时重定向每一行
 b
、永久重定向所有命令
1
、临时重定向
这里搞懂一个问题:
重定向到某个文件描述符时,必须在该文件描述符前面加
&
[root@localhost ~]# cat test
#!/bin/bash
echo "this is an error"
>&2
echo "this is normal output"
[root@localhost ~]# ./test
this is an error
this is normal output
这个脚本运行的时候没什么异常,因为重定向到错误文件描述符的内容默认也是从屏幕输出
但再次允许脚本并把标准输出重定向到normalfile文件时发现只剩下标准错误信息输出。
[root@localhost ~]# ./test 1> normalfile
this is an error
[root@localhost ~]# cat normalfile
this is normal output
2)、永久重定向
使用
exec命令可以通知shell在执行脚本时重定向特定的文件描述符:
例:
[root@localhost ~]# cat test
#!/bin/bash
exec 1> testout
       
把所有标准输出重定向到
testout
文件
exec 2> testerr
         
把所有错误输出重定向到
testerr
文件
date
date "%F %D %M"
cat /root/notexitfile
echo "do you understand?"
[root@localhost ~]# cat testout
2011年 07月 29日 星期五 22:30:49 EDT
do you understand?
[root@localhost ~]# cat testerr
date: invalid date “%F %D %M”
cat: /root/notexitfile: 没有那个文件或目录
 
三、创建自己的重定向
      
在脚本中重定向输入和输出时并不局限于
3
种默认的文件描述符,
shell
中最多可以有
9
个打开的文件描述符,其他
6
3~8
可以应用到任何文件然后在脚本中使用。
1、创建输出文件描述符
使用exec命令
例:
[root@localhost ~]# cat test
#!/bin/bash
exec 3>testout
              
创建了文件描述符
3
echo "this should display on the monitor"
echo "this should be stored in the file"
>&3
    
重定向到文件描述符
3
echo "this should be back on the monitor"
[root@localhost ~]# ./test
this should display on the monitor
this should be back on the monitor
[root@localhost ~]# cat testout
this should be stored in the file
这样就可以将普通信息输出到monitor ,将特殊信息输出到指定文件了。
2、重定向文件描述符
例:
[root@localhost ~]# cat testout
this should store in the testout file
along with this line
[root@localhost ~]# cat test
#!/bin/bash
exec 3>&1
                 
把文件描述符
3
重定向到
1
,就是
STDOUT
,也就是任何发送到文件描述符
3                 
                                的输出都将显示在monitor上
exec 1>testout           把STDOUT重定向到testout文件
 
echo "this should store in the testout file"
echo "along with this line"
 
exec 1>&3
      
把下面的
STDOUT
重定向到文件描述符
3
,而
3
已经被重定向了
1
,所以还是输出
                             
monitor
 echo "now things should be back to normal"
 
[root@localhost ~]# ./test
now things should be back to normal
[root@localhost ~]# cat testout
this should store in the testout file
along with this line
 
3、创建输入文件描述符
例:
[root@localhost ~]# cat testfile
 first line
 second line
 third line
[root@localhost ~]# cat test
#!/bin/bash
exec 6<&0
                          
使用文件描述符
6
保留
STDIN
的位置
exec 0<testfile
                  
STDIN
重定向到文件
count=1
while read line
do
 echo "Line $count : $line"
 count=$[ $count + 1 ]
done
 
exec 0<&6
                
读完所有行后再将
STDIN
设回原来的位置使
read
命令能正常得到键盘输入
read -p "Are you done now?" answer
case $answer in
Y|y ) echo "Goddbey";;
N|n ) echo "sorry,this is the end";;
esac
[root@localhost ~]# ./test
Line 1 : first line
Line 2 : second line
Line 3 : third line
Are you done now?y
Goddbey
 
4、关闭文件描述符
关闭文件描述符,只需将它重定向到特殊符号 :
&-
[root@localhost ~]# cat test
#!/bin/bash
exec 3>&1
echo "this is a test line of data" >&3
exec 3>&-
                   
这条命令前的描述符
3
是可用的。
echo "again ,test" >&3
[root@localhost ~]# ./test
this is a test line of data
./test: line 5: 3: 错误的文件描述符
 
5、列出开放的文件描述符
        
有时候需要跟踪那个文件描述符重定向到了哪个位置。
bash shell
提供了
lsof
命令来列出整个
Linux
系统上所有开放的文件描述符。但这是个有争议的功能,因为它会向非系统管理员提供有关系统的信息,所以有些
Linux
系统会隐藏该命令。要使用普通用户帐号允许这个命令,必须使用完全路径名。
[root@localhost ~]#
/usr/sbin/lsof
          
该命令会产生大量的输出,显示
Linux
系统上当前开放的每个文件的相关信息,包括所有后台运行的进程、登录到系统的用户账户。
  
常用参数:
-p : 指定进程ID
-d :指定要显示的文件描述符编号
$$ : 特殊环境变量,确定进程的当前PID
;
[root@localhost ~]# cat test
#!/bin/bash
exec 6<testfile
exec 3>&1
echo "this is a test line of data" >&3
/usr/sbin/lsof -a -p $$ -d0,1,2,3,4,5,6
[root@localhost ~]# ./test
this is a test line of data
COMMAND PID USER   FD   TYPE DEVICE SIZE    NODE NAME
test    6561 root    0u   CHR 136,0            2 /dev/pts/0
test    6561 root    1u   CHR 136,0            2 /dev/pts/0
test    6561 root    2u   CHR 136,0            2 /dev/pts/0
test    6561 root    3u   CHR 136,0            2 /dev/pts/0
test    6561 root    6r   REG 253,0   37 2590456 /root/testfile
COMMAND
:
进程中命令名称的前九个字符
 FD
:文件描述符编号和访问类型(
r:
读取;
w-
写入;
u-
读取
/
写入)
 TYPE :字符类型
(CHR:字符、
BLK
:块、
DIR:
目录、
REG
:常规文件)
SIZE:如果可用则为文件大小
SIZE:文件节点编号
 
6、禁止命令输出
黑洞文件:
/dev/null
[root@localhost ~]# ls -a > /dev/null
                   
禁止输出
[root@localhost ~]# cat /dev/null > testfile
      
清空文件
[root@localhost ~]# cat testfile
 
7、命令tee
有时候需要把输出
同时发送到
monitor
和指定文件,这时不用做两次重定向,只需使用tee命令
例:
[root@localhost ~]# date | tee testfile
2011年 07月 30日 星期六 09:29:13 EDT
[root@localhost ~]# cat testfile
2011年 07月 30日 星期六 09:29:13 EDT
注意:tee命令会覆盖输出文件,需要追加内容时需要加 -a参数
[root@localhost ~]# who | tee -a testfile
root     :0           2011-07-30 07:22
root     pts/0        2011-07-30 07:23 (:0.0)
[root@localhost ~]# cat testfile
2011年 07月 30日 星期六 09:29:13 EDT
root     :0           2011-07-30 07:22
root     pts/0        2011-07-30 07:23 (:0.0)