文章目录
1.理解输入和输出
在Linux系统上,有如下两种方式显示脚本输出的方法:
- 在显示器屏幕上显示输出
- 将输出重定向到文件
这两种输出方法有个共同的特点:若显示则都显示,若不显示则一点都不显示。
如何做到显示只想显示的一部分到屏幕或者文件呢?
可参考标准文件描述符和重定向错误两小节。
1.1 标准文件描述符
Linux用文件描述符(file descriptor)来标识每个文件对象。
文件描述符是一个非负整数,可以标识会话中打开的文件。
每个进程一次最多可以有九个文件描述符。
ps: bash shell 保留了前三个描述符(0,1,2)。如下表所示:
1.2 重定向错误(STDERR)
Q:如何重定向错误?
A:重定向错误只要在使用使用重定向符号时定义STDERR文件描述符就可以了。
实现方法如下:
方法 | 例程 | 实现和注意 |
---|---|---|
只重定向错误 | ls -la a 2> error.log |
将文件描述符放在重定向符号之前,注意,必须紧紧挨着 |
重定向错误和数据 | ls -la a b 2> error.log 1> info.log |
同时重定向错误和数据,必须使用两个重定向符号。 也要注意文件描述符和重定向符要紧紧挨着 |
ps:也可以将STDERR和STDOUT的输出重定向到同一个文件,那就要使用特殊的重定向符号&>
eg:ls -la a b &> all.log
注意:bash shell 在&>
这个重定向符上自动赋予了错误消息更高的级别,因此错误消息会在文件前列。
2.在脚本中
重定向输出–临时or永久
2.1 临时重定向
在脚本中临时重定向和在命令行中略有不同。
后者是文件描述符紧紧跟着重定向符,而前者必须是重定向符号紧紧跟着文件描述符,同时,中间必须紧紧穿插一个&
。其格式如下:
echo "error" >&2
ps:若在运行脚本时重定向了STDERR,脚本中所有导向STDERR的文本都会被重定向。
2.2 永久重定向–exec命令
exec命令会启动一个新shell并将指定的文件描述符重定向到文件。
脚本中将发送给这个文件描述符的所有输出都会被重定向到文件。
如:
exec 2>error.log
echo "error"
3.在脚本中重定向输入–在脚本中从待处理文件中读取数据的绝妙办法
exec命令也允许将STDIN重定向到Linux文件中。
其格式如下:
exec 0< testfile #重定向shell应该从testfile中获取数据,而不是STDIN。
4.创建自己的重定向–3到8的文件描述符
4.1 创建输出文件描述符
可以用exec命令来给输出分配文件描述符。
和标准的文件描述符一样,一旦将另一个文件描述符分配给一个文件,这个重定向就会一直有效,直到你重新分配。
ps:可以使用exec命令来将输出追加到现有文件中。eg:exec 3>>test13out
4.2 重定向文件描述符
重定向文件描述符的关键是对文件描述符的重定向。
可参考如下加深理解:
eg:
$ cat test14
#!/bin/bash
# storing STDOUT, then coming back to it
exec 3>&1 # 将自定义的文件描述符3重定向到STDOUT,即文件描述符1
exec 1>test14out # 将文件描述符1的输出重定向到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"
$
$ ./test14
Now things should be back to normal
$ cat test14out
This should store in the output file
along with this line.
$
4.3 创建输入文件描述符
在重定向到文件之前,先将STDIN文件描述符保存到另外一个文件描述符,然后在读取完文件之后再将STDIN恢复到它原来的位置(此做法和重定向输出文件描述符是同样的办法)。
eg:
$ cat test15
#!/bin/bash
# redirecting input file descriptors
exec 6<&0 #告诉自定义文件描述符6 以STDIN 0 作为输入
exec 0< testfile #告诉STDIN 0 以文件testfile作为输入,而不再是键盘,这样最终6也会得到文件里的内容
count=1
while read line
do
echo "Line #$count: $line"
count=$[ $count + 1 ]
done
exec 0<&6 #恢复:告诉STDIN 0 以文件描述符6作为输入,因为之前6是以STDIN 0作为输入的,现在也就恢复了
read -p "Are you done now? " answer
case $answer in
Y|y) echo "Goodbye";;
N|n) echo "Sorry, this is the end.";;
esac
$ ./test15
Line #1: This is the first line.
Line #2: This is the second line.
Line #3: This is the third line.
Are you done now? y
Goodbye
$
4.4 创建读写文件描述符–<>
创建读写文件描述符即是同时使用重定向输入和输出符号(<>
)。
eg:exec 3<> testfile
. 该例表明给自定义文件描述符3创建读写。
ps:当打开单个文件描述符来作为输入和输出的时候需要注意。
由于是对同一个文件进行数据读写,shell会维护一个内部指针,指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。 因此,对同一个文件进行数据读写,可能会对文件数据造成丢失。
eg:
$ cat test16
#!/bin/bash
# testing input/output file descriptor
exec 3<> testfile #对自定义文件描述符3创建读写
read line <&3 #读取第一行 读
echo "Read: $line" #显示第一行
echo "This is a test line" >&3 #将指定内容输出到自定义文件描述符3 写
$ cat testfile #直接查看文件内容
This is the first line.
This is the second line.
This is the third line.
$ ./test16 #执行脚本
Read: This is the first line.
$ cat testfile #执行脚本后在查看文件内容,丢失了second行
This is the first line.
This is a test line
ine.
This is the third line.
$
4.5 关闭文件描述符–重定向到特殊符号&-
通常,shell会在脚本退出时自动关闭新的文件描述符。
Q:如何提前关闭文件描述符?
A:要关闭文件描述符,将它重定向到特殊符号&-
。其脚本格式如:exec 3>&-
ps:
- 一旦关闭了文件描述符,就不能在脚本中向它写入任何数据,否则shell会生成错误消息。
- 如果随后你在脚本中打开了同一个输出文件,shell会用一个新文件来替换已有文件。这意味着如果你输出数据,它就会覆盖已有文件。
5.列出打开的文件描述符–lsof
lsof命令会列出整个Linux系统打开的所有文件描述符。
lsof常用命令行选项和参数有-p
(允许指定进程的ID,即PID)和-d
(允许指定要显示的文件描述编号)
ps:特殊环境变量$$
能知道进程的当前PID。
eg:非书中图
lsof的默认输出解释如下:
列 | 描述 |
---|---|
COMMAND | 正在运行的命令的前9个字符 |
PID | 进程的PID |
USER | 进程属主的登录名 |
FD | 文件描述符号以及访问类型(u表示读写,r表示读,w表示写) |
TYPE | 文件的类型(CHR代表字符型,BLK代表块型,DIR代表目录,REG代表常规文件) |
DEVICE | 设备的设备号(主设备号和次设备号) |
SIZE/OFF | 如果有的话,表示文件大小 |
NODE | 本地文件的节点号 |
NAME | 文件名 |
6.阻止命令输出–重定向输出到/dev/null
在Linux系统上,null文件的标准位置是/dev/null。若重定向到该位置,则不管什么数据都将被丢弃,不会显示。(可以理解成windows的回收站,但并不一样)
Q:如何快速清除一个文件的内容?
A:可借助重定向输入,即将在输入重定向中将/dev/null作为输入文件。由于/dev/null不包含任何内容,则可以快速清除一个文件的内容。
ps:在输入重定向中将/dev/null作为输入文件,是清除日志文件的一个常用方法,因为日志文件不需要删除,只需要清除以前的内容即可。
7.创建临时文件–mktemp
Linux系统中,使用/tmp目录来存放不需要永久保留的文件。这是因为大多数Linux发行版配置了系统在启动时自动删除/tmp目录下的文件。
ps:系统上的任何账户都对/tmp目录有读写权限。
7.1 创建本地临时文件
默认情况下,mktemp命令会在本地目录中创建一个文件。
Q:如何在本地目录中创建一个临时文件?
A:指定一个文件名模板即可。模板可以包含任意文本文件名,但需要在末尾加上6个X。
如下所示:
mktemp test.XXXXXX
mktemp会用6个随机的字符码替换掉这6个X。从而保证文件名在目录中是唯一的。
7.2 在/tmp目录创建临时文件–mktemp -t
-t选项会强制mktemp命令来在系统的临时目录(/tmp)来创建该文件。mktemp命令会返回用来创建临时文件的全路径,而不是只有文件名。
7.3 创建临时目录–mktemp -d
-d选项告诉mktemp命令来创建一个临时目录而不是临时文件。
ps:mktemp -d 创建的临时目录在本地而非在/tmp目录下。
8.记录消息–tee
tee命令相当于管道的一个T型接头。它将从STDIN过来的数据同时发往STDOUT和tee命令行所指定的文件名。
其格式如下:
tee testfile
ps:默认情况下,tee命令会在每次使用时覆盖输出文件内容。可使用-a
选项将数据追加到文件。
申明:文中没特殊注明,图皆来自Linux命令行与shell脚本编程大全<第三版>。