Skip to content

呈现数据

理解输入输出

  • 在显示器上输出
  • 输出重定向到文件之中

都是对于全部的操作

标准文件描述

Linux把每一个对象当做文件处理, 包括输入输出进程, Linux使用文件描述符来表示每一个文件, 文件描述是一个非负数

可以唯一标识会话中打开的文件。每个进程一次最多可以有九个文件描述符

bash shell保留了前三个文件描述符(0、1和2)

文件描述符缩写描述
0STDIN标准输入
1STDOUT标准输出
2STDERR标准错误

STDIN

标准输入, 为键盘shell从STDIN得到的数据对应键盘的输入, 在使用重定向符号<的时候, Linux会用文件替换标准输入

没有指定输入的话, cat接收键盘上的输入

可以使用重定向把数据输入到任何接收标准输入的地方

STDOUT

标准输出, 在终端上就是显示器, 所有的输出都会定义为标准输出

通过输出重定向符号,通常会显示到显示器的所有输出会被shell重定向到指定的重定向文件。你也可以将数据追加到某个文件。这可以用>>符号来完成

输出的错误信息不会输入到文件中

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test1.sh > testout
./test1.sh: 2: asd:未找到命令
./test1.sh: 3: das:未找到命令
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testout

STDERR

代表描述符的标准错误输出, 运行中的错误都会输出到这里, 通常情况下指向的是显示器, 不会随着重定向符号发生改变

重定向错误

在重定向的时候使用STDERR的描述符

只重定向错误

错误信号的文件描述符紧挨着重定向符号放置

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test1.sh 2> testout
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testout 
./test1.sh: 2: asd:未找到命令
./test1.sh: 3: das:未找到命令

可以用来区分正确的输出以及错误的输出

重定向错误和数据

如果想重定向错误和正常的信息, 要使用两个重定向符号

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ ls
test1.sh  testout
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ ls -ah test1.sh badtest 2>test2 1>test3 
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test2
ls: 无法访问 'badtest': 没有那个文件或目录
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test3
test1.sh
  • 如果要输出到一个文件, 可以使用&>
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ ls -ah test1.sh badtest &>test2
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test2
ls: 无法访问 'badtest': 没有那个文件或目录
test1.sh

相较于标准输出, 这种方式会把错误信息的优先级设置为高于普通输出, 显示在最上边

在脚本中重定向输出

在脚本中可以使用STDOUT和STDIN文件描述在多个位置生成输出, 只要简单地重定向相应的文件就可以了

  • 临时重定向
  • 永久重定向脚本所有命令

临时重定向

有意在脚本中生成错误信息, 使用输出重定向符来将输出信息重定向到STDERR文件描述符, 必须在文件描述符数字之前加一个&

bash
echo"This is an error message" >&2
bash
  1 #!/bin/bash                                                                           
  2 
  3 echo "This is an error" >&2
  4 echo "This is an normal output"


jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test2.sh 
This is an error
This is an normal output
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ test2.sh > test2
This is an error
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ test2.sh 2> test2
This is an normal output

永久重定向

脚本中有大量的需要重定向的内容就会很麻烦, 但是你可以使用exec命令告诉shell在脚本执行期间重定向某个特定文件描述符

bash
  1 #!/bin/bash                                                                           
  2 
  3 exec 2>testerror
  4 
  5 echo "This is the start of script"
  6 echo "now redirecting all output to another location"
  7 
  8 exec 1>testout
  9 
 10 echo "This output should go to the testout file"
 11 echo "but this should go to the testerror file" >&2
 12 

jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test4.sh 
This is the start of script
now redirecting all output to another location
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testerror 
but this should go to the testerror file
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testout 
This output should go to the testout file

一旦重定向了STDOUT或STDERR,就很难再将它们重定向回原来的位置。

在脚本中重定向输入

exec命令允许你将STDIN重定向到Linux系统上的文件中

bash
  1 #!/bin/bash                                                                           
  2 
  3 exec 0< testfile
  4 count=1
  5 while read line
  6 do
  7     echo "Line #$count: $line"
  8     count=$[ $count + 1 ]
  9 done


jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ test5.sh 
Line #1: 11111
Line #2: 2222222
Line #3: 33333333333
Line #4: 44444444444444444

创建自己的重定向

可以吧文件描述符中的其他几个分配给其他的文件

创建输出文件描述符

bash
  1 #!/bin/bash                                                                           
  2 
  3 exec 3>test6out
  4 echo "This shoulf display on the monitor"
  5 echo "stored in the file " >&3
  6 echo "back"

jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test6.sh 
This shoulf display on the monitor
back
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test6out 
stored in the file

也可以使用追加exec 3>>testfile, 在文件切换之后使用

重定向文件描述符

你可以分配另外一个文件描述符给标准文件描述符,反之亦然。这意味着你可以将STDOUT的原来位置重定向到另一个文件描述符,然后再利用该文件描述符重定向回STDOUT。

bash
  1 #!/bin/bash                                                                           
  2 
  3 exec 3>&1
  4 exec 1>test7out
  5 echo "This shoule store in the output file"
  6 echo "along with this line"
  7 
  8 exec 1>&3
  9 echo "Now thing should back"

jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test7.sh 
Now thing should back
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test7out 
This shoule store in the output file
along with this line

创建文件输入描述符

bash
$ cat test15 
#!/bin/bash 
# redirecting input file descriptors 
exec 6<&0 
exec 0< testfile 
count=1 
while read line 
do
	echo "Line #$count: $line"    
	count=$[ $count + 1 ] 
done 
exec 0<&6 
read -p "Are you done now? " answer 
case $answer in
Y|y) echo "Goodbye";; 
N|n) echo "Sorry, this is the end.";; 
esac

创建读写文件描述符

打开单个文件描述符来作为输入和输出

shell会维护一个内部指针,指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。

bash
  1 #!/bin/bash                                                                           
  2 exec 3<> testfile
  3 read line <&3
  4 echo "line: $line" 
  5 echo "This is a test line " >&3
  
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test8.sh 
line: 11111
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile 
11111
This is a test line 
4444444444444444

关闭文件描述符

shell会在脚本退出时自动关闭它们。然而在有些情况下,你需要在脚本结束前手动关闭文件描述符

要关闭文件描述符,将它重定向到特殊符号&-。脚本中看起来如下:

bash
exec 3>&-

一旦关闭了文件描述符,就不能在脚本中向它写入任何数据,否则shell会生成错误消息

如果随后你在脚本中打开了同一个输出文件,shell会用一个新文件来替换已有文件。这意味着如果你输出数据,它就会覆盖已有文件

列出打开的文件描述符

bash
lsof

许多Linux系统隐藏了该命令,这样用户就不会一不小心就发现了

有大量的命令行选项和参数可以用来帮助过滤lsof的输出。最常用的有-p和-d,前者允许指定进程ID(PID),后者允许指定要显示的文件描述符编号

要想知道进程的当前PID,可以用特殊环境变量$$(shell会将它设为当前PID)。-a选项用来对其他两个选项的结果执行布尔AND运算,这会产生如下输出

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ lsof -a -p $$ -d 0,1,2
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
bash    5039 jiao    0u   CHR  136,0      0t0    3 /dev/pts/0
bash    5039 jiao    1u   CHR  136,0      0t0    3 /dev/pts/0
bash    5039 jiao    2u   CHR  136,0      0t0    3 /dev/pts/0

因为STDIN、STDOUT和STDERR文件描述符都指向终端,所以输出文件的名称就是终端的设备名

  • COMMAND 正在运行的命令名的前9个字符
  • PID 进程的PID
  • USER 进程属主的登录名
  • FD 文件描述符号以及访问类型(r代表读,w代表写,u代表读写)
  • TYPE 文件的类型(CHR代表字符型,BLK代表块型,DIR代表目录,REG代表常规文件)
  • DEVICE 设备的设备号(主设备号和从设备号)
  • SIZE/OFF 如果有的话,表示文件的大小
  • NODE 本地文件的节点号
  • NAME 文件名

阻止命令输出

如果在运行在后台的脚本出现错误消息,shell会通过电子邮件将它们发给进程的属主

可以将STDERR重定向到一个叫作null文件的特殊文件

在Linux系统上null文件的标准位置是/dev/null。你重定向到该位置的任何数据都会被丢掉,不会显示

也可以在输入重定向中将/dev/null作为输入文件。由于/dev/null文件不含有任何内容,程序员通常用它来快速清除现有文件中的数据,而不用先删除文件再重新创建

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile
11111
This is a test line 
4444444444444444
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat /dev/null > testfile 
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile

创建临时文件

Linux使用/tmp文件夹存放不需要永久保存的文件, 大多数Linux发行版配置了系统在启动时自动删除/tmp目录的所有文件

任何用户账户都有权限在读写/tmp目录中的文件。这个特性为你提供了一种创建临时文件的简单方法,而且还不用操心清理工作

mktemp命令可以在/tmp目录中创建一个唯一的临时文件。shell会创建这个文件,但不用默认的umask值(参见第7章)。它会将文件的读和写权限分配给文件的属主,并将你设成文件的属主。一旦创建了文件,你就在脚本中有了完整的读写权限,但其他人没法访问它(当然,root用户除外)

创建本地临时文件

mktemp会在本地目录中创建一个文件。要用mktemp命令在本地目录中创建一个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6个X就行了

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ mktemp testing.XXXXXX
testing.FaEYfs

输出的就是创建的文件名, 可以保存在变量之中在后面使用

在/tmp目录创建临时文件

-t选项会强制mktemp命令来在系统的临时目录来创建该文件。在用这个特性时,mktemp命令会返回用来创建临时文件的全路径,而不是只有文件名

bash
  1 #!/bin/bash                                                                           
  2 
  3 tempfile=$(mktemp -t tmp.XXXXXX)
  4 echo "This is a test file." > $tempfile
  5 echo "This is the second line." >> $tempfile
  6 
  7 echo "The temp file is located at: $tempfile"
  8 cat $tempfile
  9 rm -f $tempfile

创建临时目录

-d选项告诉mktemp命令来创建一个临时目录而不是临时文件。这样你就能用该目录进行任何需要的操作了,比如创建其他的临时文件

bash
  1 #!/bin/bash                                                                           
  2 
  3 tempdir=$(mktemp -d dir.XXXXXX)		# 在本地创建一个文件夹
  4 cd $tempdir
  5 tempfile1=$(mktemp temp.XXXXXX)
  6 tempfile2=$(mktemp temp.XXXXXX)
  7 exec 7> $tempfile1
  8 exec 8> $tempfile2
  9 
 10 echo "Sending data to dictory $tempdir"
 11 echo "This is a test line of data for $tempfile1" >&7
 12 echo "This is a test line of data for $tempfile2" >&8

记录消息

把输出的信息同时发送到显示器以及日志文件

bash
tee filename

它将从STDIN过来的数据同时发往两处。一处是STDOUT,另一处是tee命令行所指定的文件名

写入的时候会覆盖原来的内容, 要是追加的话用-a

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ who | tee testfile
jiao     tty2         2022-08-07 21:10 (tty2)
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile 
jiao     tty2         2022-08-07 21:10 (tty2)
bash
  1 #!/bin/bash                                                                           
  2 
  3 tempfile=test12out
  4 
  5 echo $(date)| tee $tempfile
  6 echo $(ls -alh)| tee -a $tempfile
  7 echo $(who)| tee -a $tempfile

示例

bash
$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}		# 读取文件

cat >> $outfile << EOF 先把输入的内容使用cat打印, 然后输入到文件中