Linux命令行-第15章@shell编程-呈现数据③创建临时文件
第 15章 呈现数据
本章内容
再探重定向
标准输入和输出
报告错误
丢弃数据
创建日志文件
到来显示信息。第11章中演示了如何将命令的输出重定向到文件中。本章将会展开这个目前为止,本书中出现的脚本都是通过将数据打印在屏幕上或将数据重定向到文件中主题,演示如何将脚本的输出重定向到Linux系统的不同位置。
15.7 创建临时文件
Linux系统有特殊的目录,专供临时文件使用。Linux使用/tmp目录来存放不需要永久保留的文件。大多数Linux发行版配置了系统在启动时自动删除/tmp目录的所有文件。
系统上的任何用户账户都有权限在读写/tmp目录中的文件。这个特性为你提供了一种创建临时文件的简单方法,而且还不用操心清理工作。
有个特殊命令可以用来创建临时文件。mktemp命令可以在/tmp目录中创建一个唯一的临时文件。shell会创建这个文件,但不用默认的umask值(参见第7章)。它会将文件的读和写权限分配给文件的属主,并将你设成文件的属主。一旦创建了文件,你就在脚本中有了完整的读写权限,但其他人没法访问它(当然,root用户除外)。
15.7.1 创建本地临时文件
默认情况下,mktemp会在本地目录中创建一个文件。要用mktemp命令在本地目录中创建一个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6个X就行了。
$ mktemp testing.XXXXXX
$ ls -al testing*
-rw------- 1 rich rich 0 Oct 17 21:30 testing.UfIi13
$
mktemp命令会用6个字符码替换这6个X,从而保证文件名在目录中是唯一的。你可以创建多个临时文件,它可以保证每个文件都是唯一的。
$ mktemp testing.XXXXXX
testing.1DRLuV
$ mktemp testing.XXXXXX
testing.lVBtkW
$ mktemp testing.XXXXXX
testing.PgqNKG
$ ls -l testing*
-rw------- 1 rich rich 0 Oct 17 21:57 testing.1DRLuV -rw------- 1 rich rich 0 Oct 17 21:57 testing.PgqNKG -rw------- 1 rich rich 0 Oct 17 21:30 testing.UfIi13 -rw------- 1 rich rich 0 Oct 17 21:57 testing.lVBtkW
$
如你所看到的,mktemp命令的输出正是它所创建的文件的名字。在脚本中使用mktemp命令时,可能要将文件名保存到变量中,这样就能在后面的脚本中引用了。
$ cat test19
#!/bin/bash
# creating and using a temp file
tempfile=$(mktemp test19.XXXXXX)
exec 3>$tempfile
echo "This script writes to temp file $tempfile"
echo "This is the first line" >&3
echo "This is the second line." >&3
echo "This is the last line." >&3
exec 3>&-
echo "Done creating temp file. The contents are:"
cat $tempfile
rm -f $tempfile 2> /dev/null
$ ./test19
This script writes to temp file test19.vCHoya
Done creating temp file. The contents are:
This is the first line
This is the second line.
This is the last line.
$ ls -al test19*
-rwxr--r-- 1 rich rich 356 Oct 29 22:03 test19*
$
这个脚本用mktemp命令来创建临时文件并将文件名赋给$tempfile变量。接着将这个临时文件作为文件描述符3的输出重定向文件。在将临时文件名显示在STDOUT之后,向临时文件中写入了几行文本,然后关闭了文件描述符。最后,显示出临时文件的内容,并用rm命令将其删除。
15.7.2 在/tmp目录创建临时文件
-t选项会强制mktemp命令来在系统的临时目录来创建该文件。在用这个特性时,mktemp命令会返回用来创建临时文件的全路径,而不是只有文件名。
$ mktemp -t test.XXXXXX
/tmp/test.xG3374
$ ls -al /tmp/test*
-rw------- 1 rich rich 0 2014-10-29 18:41 /tmp/test.xG3374
$
由于mktemp命令返回了全路径名,你可以在Linux系统上的任何目录下引用该临时文件,不管临时目录在哪里。
$ cat test20
#!/bin/bash
# creating a temp file in /tmp
tempfile=$(mktemp -t tmp.XXXXXX)
echo "This is a test file." > $tempfile
echo "This is the second line of the test." >> $tempfile
echo "The temp file is located at: $tempfile" cat $tempfile
rm -f $tempfile
$ ./test20
The temp file is located at: /tmp/tmp.Ma3390
This is a test file.
This is the second line of the test.
$
在mktemp创建临时文件时,它会将全路径名返回给变量。这样你就能在任何命令中使用该值来引用临时文件了。
15.7.3 创建临时目录
-d选项告诉mktemp命令来创建一个临时目录而不是临时文件。这样你就能用该目录进行任何需要的操作了,比如创建其他的临时文件。
$ cat test21
#!/bin/bash
# using a temporary directory
tempdir=$(mktemp -d dir.XXXXXX) cd $tempdir
tempfile1=$(mktemp temp.XXXXXX) tempfile2=$(mktemp temp.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 $ ./test21
Sending data to directory dir.ouT8S8
$ ls -al
total 72
drwxr-xr-x 3 rich rich 4096 Oct 17 22:20 ./
drwxr-xr-x 9 rich rich 4096 Oct 17 09:44 ../
drwx------ 2 rich rich 4096 Oct 17 22:20 dir.ouT8S8/ -rwxr--r-- 1 rich rich 338 Oct 17 22:20 test21*
$ cd dir.ouT8S8
[dir.ouT8S8]$ ls -al
total 16
drwx------ 2 rich rich 4096 Oct 17 22:20 ./
drwxr-xr-x 3 rich rich 4096 Oct 17 22:20 ../
-rw------- 1 rich rich 44 Oct 17 22:20 temp.N5F3O6 -rw------- 1 rich rich 44 Oct 17 22:20 temp.SQslb7 [dir.ouT8S8]$ cat temp.N5F3O6
This is a test line of data for temp.N5F3O6
[dir.ouT8S8]$ cat temp.SQslb7
This is a test line of data for temp.SQslb7
[dir.ouT8S8]
$
这段脚本在当前目录创建了一个目录,然后它用cd命令进入该目录,并创建了两个临时文件。之后这两个临时文件被分配给文件描述符,用来存储脚本的输出。
15.8 记录消息
将输出同时发送到显示器和日志文件,这种做法有时候能够派上用场。你不用将输出重定向两次,只要用特殊的tee命令就行。
tee命令相当于管道的一个T型接头。它将从STDIN过来的数据同时发往两处。一处是STDOUT,另一处是tee命令行所指定的文件名:
tee filename
由于tee会重定向来自STDIN的数据,你可以用它配合管道命令来重定向命令输出。
$ date | tee testfile
Sun Oct 19 18:56:21 EDT 2014 $ cat testfile
Sun Oct 19 18:56:21 EDT 2014
$
输出出现在了STDOUT中,同时也写入了指定的文件中。注意,默认情况下,tee命令会在每次使用时覆盖输出文件内容。
$ who | tee testfile
rich pts/0 2014-10-17 18:41 (192.168.1.2)
$ cat testfile
rich pts/0 2014-10-17 18:41 (192.168.1.2)
$
如果你想将数据追加到文件中,必须用-a选项。
$ date | tee -a testfile
Sun Oct 19 18:58:05 EDT 2014
$ cat testfile
rich pts/0 2014-10-17 18:41 (192.168.1.2)
Sun Oct 19 18:58:05 EDT 2014
$
利用这个方法,既能将数据保存在文件中,也能将数据显示在屏幕上。
$ cat test22
#!/bin/bash
# using the tee command for logging
tempfile=test22file
echo "This is the start of the test" | tee $tempfile
echo "This is the second line of the test" | tee -a $tempfile echo "This is the end of the test" | tee -a $tempfile
$ ./test22
This is the start of the test
This is the second line of the test
This is the end of the test
$ cat test22file
This is the start of the test
This is the second line of the test
This is the end of the test
$
现在你就可以在为用户显示输出的同时再永久保存一份输出内容了。
15.9 实例
文件重定向常见于脚本需要读入文件和输出文件时。这个样例脚本两件事都做了。它读取.csv格式的数据文件,输出SQL INSERT语句来将数据插入数据库(参见第25章)。
shell脚本使用命令行参数指定待读取的.csv文件。.csv格式用于从电子表格中导出数据,所以你可以把数据库数据放入电子表格中,把电子表格保存成.csv格式,读取文件,然后创建INSERT语句将数据插入MySQL数据库。
脚本内容如下。
$cat test23
#!/bin/bash
# read file and create INSERT statements for MySQL
outfile='members.sql'
IFS=','
while read lname fname address city state zip
do
cat >> $outfile << EOF
INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('$lname', '$fname', '$address', '$city', '$state', '$zip');
EOF
done < ${1}
$
这个脚本很短小,这都要感谢有了文件重定向!脚本中出现了三处重定向操作。while循环
使用read语句(参见第14章)从数据文件中读取文本。注意在done语句中出现的重定向符号:
done < ${1}
当运行程序test23时,$1代表第一个命令行参数。它指明了待读取数据的文件。read语句会使用IFS字符解析读入的文本,我们在这里将IFS指定为逗号。
脚本中另外两处重定向操作出现在同一条语句中:
cat >>
o
u
t
f
i
l
e
<
<
E
O
F
这条语句中包含一个输出追加重定向(双大于号)和一个输入追加重定向(双小于号)。输出重定向将
c
a
t
命令的输出追加到由
outfile << EOF 这条语句中包含一个输出追加重定向(双大于号)和一个输入追加重定向(双小于号)。输出重定向将cat命令的输出追加到由
outfile<<EOF这条语句中包含一个输出追加重定向(双大于号)和一个输入追加重定向(双小于号)。输出重定向将cat命令的输出追加到由outfile变量指定的文件中。cat命令的输入不再取自标准输入,而是被重定向到脚本中存储的数据。EOF符号标记了追加到文件中的数据的起止。
INSERT INTO members (lname,fname,address,city,state,zip) VALUES (‘
l
n
a
m
e
′
,
′
lname', '
lname′,′fname’, ‘
a
d
d
r
e
s
s
′
,
′
address', '
address′,′city’, ‘
s
t
a
t
e
′
,
′
state', '
state′,′zip’);
上面的文本生成了一个标准的SQL INSERT语句。注意,其中的数据会由变量来替换,变量中内容则是由read语句存入的。
所以基本上while循环一次读取一行数据,将这些值放入INSERT语句模板中,然后将结果输出到输出文件中。
在这个例子中,使用以下输入数据文件。
$ cat members.csv
Blum,Richard,123 Main St.,Chicago,IL,60601
Blum,Barbara,123 Main St.,Chicago,IL,60601
Bresnahan,Christine,456 Oak Ave.,Columbus,OH,43201 Bresnahan,Timothy,456 Oak Ave.,Columbus,OH,43201
$
运行脚本时,显示器上不会出现任何输出:
$ ./test23 < members.csv
$
但是在members.sql输出文件中,你会看到如下输出内容。
$ cat members.sql
INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Blum',
'Richard', '123 Main St.', 'Chicago', 'IL', '60601');
INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Blum',
'Barbara', '123 Main St.', 'Chicago', 'IL', '60601');
INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Bresnahan', 'Christine', '456 Oak Ave.', 'Columbus', 'OH', '43201');
INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Bresnahan', 'Timothy', '456 Oak Ave.', 'Columbus', 'OH', '43201');
$
结果和我们预想的一样!现在可以将members.sql文件导入MySQL数据表中了(参见第25章)。
15.10 小结
在创建脚本时,理解了bash shell如何处理输入和输出会给你带来很多方便。你可以改变脚本获取数据以及显示数据的方式,从而在任何环境中定制脚本。脚本的输入/输出都可以从标准输入(STDIN)/标准输出(STDOUT)重定向到系统中的任意文件。除了STDOUT,你可以通过重定向STDERR输出来重定向由脚本产生的错误消息。这可以通过重定向与STDERR输出关联的文件描述符(也就是文件描述符2)来实现。可以将STDERR输出和STDOUT输出到同一个文件中,也可以输出到完全不同的文件中。这样就可以将脚本的正常消息同错误消息分离开。
bash shell允许在脚本中创建自己的文件描述符。你可以创建文件描述符3~9,并将它们分配给要用到的任何输出文件。一旦创建了文件描述符,你就可以利用标准的重定向符号将任意命令的输出重定向到那里。
bash shell也允许将输入重定向到一个文件描述符,这给出了一种将文件数据读入到脚本中的简便途径。你可以用lsof命令来显示shell中在用的文件描述符。
Linux系统提供了一个特殊的文件(称为/dev/null)来重定向不需要的输出。Linux系统会删掉任何重定向到/dev/null文件的东西。你也可以通过将/dev/null文件的内容重定向到一个文件中来产生空文件。
mktemp命令是bash shell中一个很方便的特性,可以轻松地创建临时文件和目录。只需要给mktemp命令指定一个模板,它就能在每次调用时基于该文件模板的格式创建一个唯一的文件。也可以在Linux系统的/tmp目录创建临时文件和目录,系统启动时会清空这个特殊位置中的内容。
tee命令便于将输出同时发送给标准输出和日志文件。这样就可以在显示器上显示脚本的消息的同时,又能将它们保存在日志文件中。
在第16章中,你将了解如何控制和运行脚本。除了直接从命令行中运行之外,Linux还提供了另外几种不同的方法来运行脚本。你还将了解如何在特定时间运行脚本,以及在脚本运行时如何暂停。