文章目录
15. 呈现数据
15.1 理解输入和输出
15.1.1 标准文件描述符
Linux系统将每个对象当作文件处理。Linux用文件描述符(file descriptor)来标识每个文件对象。文件描述符是一个非负整数,可以唯一标识会话中打开的文件。每个进程一次最多可以有九个文件描述符。出于特殊目的,bash shell保留了前三个文件描述符(0、1和2)。
15.1.2 重定向错误
只重定向错误: STDERR文件描述符2必须紧紧地放在重定向符号前,否则不会工作。
#当前目录下面正常来说没有badfile,因此ls -al badfile会报错
#2>将错误输出信息重定向到test4
ls -al badfile 2> test4
使用STDOUT和STDERR消息混杂在同一输出例子,命令执行结果:
#如果test或者badtest或者test2有一个存在于当前目录,则是STDOUT,或者是STDERR输出重定向到test5
$ ls -al test badtest test2 2> test5
-rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2
$ cat test5
ls: cannot access test: No such file or directory
ls: cannot access badtest: No such file or directory
重定向错误和数据: 重定向错误和正常输出,必须用两个重定向符号,相关命令执行结果:
#列举test test2 test3 badtest三个文件信息, 将STDERR 2重定向到test6 STDOUT重定向到test7
$ ls -al test test2 test3 badtest 2> test6 1> test7
$ cat test6
ls: cannot access test: No such file or directory
ls: cannot access badtest: No such file or directory
$ cat test7
-rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2
-rw-rw-r-- 1 rich rich 0 2014-10-16 11:33 test3
如果想要将STDOUT和STDERR重定向到同一个输出文件,应该使用&>
,执行结果为:
$ ls -al test test2 test3 badtest &> test7
$ cat test7
ls: cannot access test: No such file or directory
ls: cannot access badtest: No such file or directory
-rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2
-rw-rw-r-- 1 rich rich 0 2014-10-16 11:33 test3
15.2 重定向输出
临时重定向
在脚本中将错误信息重定向到STDERR,然后在运行的时候将STDERR重定向到某个文件。
#!/bin/bash
# 将这行信息重定向到STDERR
echo "This is an error" >&2
echo "This is normal output"
执行的时候一定要重新将STDERR重定向到某个文件,才能看出区别。默认将STDERR输出到STDOUT,因此,执行及结果为:
$ ./test8 2> test9
This is normal output
$ cat test9
This is an error
永久重定向
如果脚本中有大量数据需要重定向,上述方法不适合,因为需要将每条消息输入到STDERR。可以使用exec
告诉shell在脚本执行期间重定向到某个特定文件描述符。
exec 1>testout #将STDOUT重定向到testout
exec 2>testerror #将STDERROR重定向到testerror
示例代码:
#!/bin/bash
# redirecting output to different locations
exec 2>testerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>testout
echo "This output should go to the testout file"
echo "but this should go to the testerror file" >&2 #如果不使用&2,这代表将这条消息重定向到名称为2的文件中
执行结果:
$ ./test11
This is the start of the script
now redirecting all output to another location
$ cat testout
This output should go to the testout file
$ cat testerror
but this should go to the testerror file
可以将echo语句的输出发给STDERR,一旦重定向了STDOUT或STDERR,就很难再将它们重定向回原来的位置。
15.3 在脚本中重定向输入
exec 0< filename #从文件名为filename的文件中获得输入
示例代码:
#!/bin/bash
# redirecting file input
exec 0< testfile
count=1
while read line
do
echo "Line #$count: $line"
count=$[ $count + 1 ]
done
执行结果:
$ ./test12
Line #1: This is the first line.
Line #2: This is the second line.
Line #3: This is the third line.
15.4 创建自己的重定向
15.4.1 创建输出文件描述符
#!/bin/bash
exec 3>test13out #test13out就是输出文件描述符
echo "This should display on the monitor"
echo "and this should be stored in the file" >&3
echo "Then this should be back on the monitor"
exec
追加到现有文件
exec 3>>test13out
15.4.2 重定向文件描述符
还原文件描述符的重定向,就是将重定向的文件描述符重新转换回去,看下列代码:
#!/bin/bash
# storing STDOUT, then coming back to it
exec 3>&1 #将文件描述符3重定向到STDOUT
exec 1>test14out #将STDOUT重定向到test14out文件中
echo "This should store in the output file"
echo "along with this line."
exec 1>&3 #将STDOUT重定向到文件描述符3就是将这个还原了
echo "Now things should be back to normal"
15.4.3 创建输入文件描述符
按照重定向输出描述符同样的方法重定向输入文件描述符
#!/bin/bash
exec 6<&0 #将键盘输入重定向到6号文件描述符
exec 0< testfile #将testfile作为STDIN标准输入
count=1
while read line
do
echo "Line #$count:$line"
count=$[ $count + 1 ]
done
exec 0<&6 #将6号文件描述符键盘输入返还给STDIN
read -p "Are you done now?" answer
case $answer in
Y|y) echo "Goodbye";;
N|n) echo "Sorry, This is the end.";;
esac
15.4.4 创建读写文件描述符
对同一个文件进行数据读写,shell会维护一个
内部指针,指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。
示例代码:
#!/bin/bash
exec 3<> testfile #将testfile作为3号文件描述符号的输入输出
read line <&3 #读取了第一行并打印了出来
echo "Read: $line"
echo "This is a test line" >&3 #修改了第二行的内容
执行结果:
15.4.5 关闭文件描述符
如果你创建了新的输入或输出文件描述符,shell会在脚本退出时自动关闭它们。然而在有些情况下,你需要在脚本结束前手动关闭文件描述符。运行命令:
exec fd(file descriptor)>&-
一旦关闭了文件描述符,就不能在脚本中向它写入任何数据,否则shell会生成错误消息。 在关闭文件描述符时还要注意另一件事。如果随后你在脚本中打开了同一个输出文件,shell会用一个新文件来替换已有文件。这意味着如果你输出数据,它就会覆盖已有文件。
示例代码:
#!/bin/bash
exec 3> test_new_file #将文件描述符3重定向test_new_file
echo "This is a test line of data" >&3 #将信息输入文件描述符3
exec 3>&- #停止当前文件描述符的操作
#再次打开
cat test_new_file
exec 3> test_new_file #如果将这行改为exec 3>> test_new_file则不会出现上述问题
echo "This is a new test line of data" >&3
15.5 列出打开的文件描述符
记住哪个文件描述符被重定向到了哪里很难,因此使用lsof
命令。但是它会向非系统管理员用户提供Linux系统的信息,因此一般会隐藏这条命令,需要/usr/sbin/lsof
来引用,这会打印很多的输出。
选项 | 作用 |
---|---|
-p | 指定进程ID($$一般默认当前进程) |
-d | 显示的文件描述符编号 |
-a | 一般是对操作做与运算 |
执行lsof -a -p $$ -d 0,1,2
lsof
输出详解
15.6 阻止命令输出
很多时候,会将无关痛痒的小错误也打印出来或者发送给进程的属主。为了解决这个问题,可以将STDERR重定向到null文件,null文件在linux的标准位置为/dev/null。
ls -al badfile test16 2> /dev/null
同时由于/dev/null文件不含有任何内容,程序员通常用它来快速清除现有文件中的数据,而不用先删除文件再重新创建。
cat /dev/null > testfile
15.7 创建临时文件
15.7.1 创建本地临时文件
mktemp会在本地目录中创建一个文件。命令格式为mktemp filename.XXXXXX
注意:x一定要大写
在脚本中使用mktemp必须将文件名保存到变量,然后继续引用
#!/bin/bash
tempfile=$(mktemp testing.XXXXXX)
exec 3> $tempfile #将文件描述符3重定向到tempfile
echo "This is the first line." >&3
echo "This is the second line." >&3
echo "This is the third line." >&3
exec 3>&- #关闭3号文件描述符
#执行完毕之后删除临时文件,将错误信息不打印出来
echo "Done creating temp file, the content are:"
cat $tempfile
rm -f $tempfile 2> /dev/null
15.7.2 在/tmp目录创建临时文件
-t
选项会强制mktemp命令来在系统的临时目录来创建该文件。在用这个特性时,mktemp命令会返回用来创建临时文件的全路径,而不是只有文件名。因此可以在任何脚本下使用这个路径引用这种方式生成的临时文件。
15.7.3 创建临时目录
-d
选项告诉mktemp命令来创建一个临时目录而不是临时文件。
#!/bin/bash
tempdir=$(mktemp -d dir.XXXXXX)
cd $tempdir
tempfile1=$(mktemp tmp.XXXXXX)
tempfile2=$(mktemp tmp.XXXXXX)
exec 7> $tempfile1
exec 8> $tempfile2
echo "Sending data to directory $tempdir"
echo "This is a test line of data for $tempfile1" >&7
echo "This is a test line of data for $tempfile2" >&8
15.8 记录消息
tee命令相当于管道的一个T型接头。它将从STDIN过来的数据同时发往两处。一处是STDOUT,另一处是tee命令行所指定的文件名:
tee filename
使用管道重定向数据,这样会重写testfile内容,-a
是对文件追加数据,
date | tee testfile
date | tee -a testfile