Key words 了解输入和输出     标准文件描述     在脚本中重定向输入和输出          创建自己的重定向     命令tee
 
一、了解输入和输出
我们常见的显示输出方式有两种:
a、直接在屏幕上显示输出   b 、将输出重定向到文件中
这两种方法要么生成全部数据输出,要么没有任何输出。但有时候需要直接在显示屏上显示一部分数据,在文件中显示另一部分数据。在这种情况下,最好能了解Linux处理输入输出的方式,一遍让脚本输出到正确的位置。
   标准文件描述
        Linux系统将每个对象(包括输入和输出)当作文件处理,并使用文件描述符标识每个文件对象。文件描述符是一个非负整数,可以唯一地标识会话中打开的文件。每个进程最多可以有9个打开文件的描述符。
      bash shell 为特殊需要保留了前三个文件描述符:012 分别代表标准输入、标准输出、标准错误
10   : 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个打开的文件描述符,其他63~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)