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)