shell 入门 05 呈现数据

2022-09-21  本文已影响0人  wjundong

呈现数据

标准文件描述符

Linux系统将每个对象当作文件处理。这包括输入和输出进程。Linux用文件描述符(file
descriptor)来标识每个文件对象。文件描述符是一个非负整数,可以唯一标识会话中打开
的文件。每个进程一次最多可以有九个文件描述符。出于特殊目的,bash shell保留了前三个文
件描述符(0、1和2)

这三个特殊文件描述符会处理脚本的输入和输出。shell用它们将shell默认的输入和输出导向到相应的位置。

# 1. STDIN 重定向
cat < testfile

# 2. STDOUT 重定向
ls -l > test2 

# 你也可以将数据追加到某个文件。这可以用>>符号来完成
who >> test2 

# 但是,如果你对脚本使用了标准输出重定向,你会遇到一个问题。
# 当命令生成错误消息时,shell并未将错误消息重定向到输出重定向文件
ls -al badfile > test3
# shell对于错误消息的处理是跟普通输出分开的。如果你创建了在后台模式下运行的shell脚
# 本,通常你必须依赖发送到日志文件的输出消息。用这种方法的话,如果出现了错误信息,这些
# 信息是不会出现在日志文件中的。你需要换种方法来处理

# 3. STDERR 
# shell通过特殊的STDERR文件描述符来处理错误消息。STDERR文件描述符代表shell的标准错
# 误输出。shell或shell中运行的程序和脚本出错时生成的错误消息都会发送到这个位置。
# 默认情况下,STDERR文件描述符会和STDOUT文件描述符指向同样的地方(尽管分配给它们
# 的文件描述符值不同)。也就是说,默认情况下,错误消息也会输出到显示器输出中。
# 但从上面的例子可以看出,STDERR并不会随着STDOUT的重定向而发生改变。使用脚本时,
# 你常常会想改变这种行为,尤其是当你希望将错误消息保存到日志文件中的时候。
# 你已经知道如何用重定向符号来重定向STDOUT数据。重定向STDERR数据也没太大差别,只
# 要在使用重定向符号时定义STDERR文件描述符就可以了。有几种办法实现方法

# 只重定向错误
# 可以选择只重定向错误消息,将文件描述符值放在重定向符号前。该值必须紧紧地放在重定向符号前,否则不会工作
ls -al badfile 2> output.txt

# 重定向错误和数据
# 如果想重定向错误和正常输出,必须用两个重定向符号。需要在符号前面放上待重定向数据
# 所对应的文件描述符,然后指向用于保存数据的输出文件
ls -al test badtest 2> test1 1> test2

# 另外,如果愿意,也可以将STDERR和STDOUT的输出重定向到同一个输出文件。为此bash shell提供了特殊的重定向符号&>
# 为了避免错误信息散落在输出文件中,相较于标准输出,bash shell自动赋予了错误消息更高的优先级。这样你能够集中浏览错误信息了
ls -al test badtest &> test1

在脚本中重定向输出

可以在脚本中用STDOUT和STDERR文件描述符以在多个位置生成输出,只要简单地重定向相
应的文件描述符就行了。有两种方法来在脚本中重定向输出:

# 1. 临时重定向
# 如果有意在脚本中生成错误消息,可以将单独的一行输出重定向到STDERR
# 在重定向到文件描述符时,你必须在文件描述符数字之前加一个&
# 下面 STDOUT 显示的文本显示在了屏幕上,而发送给STDERR的echo语句的文本则被重定向到了输出文件
# ./demo.sh 2> test
echo "This is an error" >&2 
echo "This is normal output"

# 2. 永久重定向
# 如果脚本中有大量数据需要重定向,那重定向每个echo语句就会很烦琐。取而代之,你可
# 以用exec命令告诉shell在脚本执行期间重定向某个特定文件描述符
# exec命令会启动一个新shell并将STDOUT文件描述符重定向到文件。脚本中发给STDOUT的所有输出会被重定向到文件
exec 1> testout 
echo "This is a test of redirecting all output" 

# 可以在脚本执行过程中重定向STDOUT
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

# 一旦重定向了STDOUT或STDERR,就很难再将它们重定向回原来的位置。如果你需要在重定
# 向中来回切换的话,有个办法可以用

在脚本中重定向输入

# 这个命令会告诉shell它应该从文件testfile中获得输入,而不是STDIN
exec 0< testfile
count=1
while read line; do
    echo "Line #$count: $line"
    count=$(($count + 1))
done

创建自己的重定向

在脚本中重定向输入和输出时,并不局限于这3个默认的文件描述符。我曾提到过,在shell
中最多可以有9个打开的文件描述符。其他6个从3~8的文件描述符均可用作输入或输出重定向。
你可以将这些文件描述符中的任意一个分配给文件,然后在脚本中使用它们。本节将介绍如何在
脚本中使用其他文件描述符。

# 1. 创建输出文件描述符
# 可以用exec命令来给输出分配文件描述符。和标准的文件描述符一样,一旦将另一个文件
# 描述符分配给一个文件,这个重定向就会一直有效,直到你重新分配。
# 将文件描述符 3 重定向到 test13out 文件, 然后在行中重定向输出到文件描述符 3
exec 3> 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

# 2. 重定向文件描述符
# 现在介绍怎么恢复已重定向的文件描述符。你可以分配另外一个文件描述符给标准文件描述
# 符,反之亦然。这意味着你可以将STDOUT的原来位置重定向到另一个文件描述符,然后再利用
# 该文件描述符重定向回STDOUT。听起来可能有点复杂,但实际上相当直接。这个简单的例子能
# 帮你理清楚。这里文件描述符3 相当于一个中间变量, 存储了文件描述符1 的 old 位置, 之后再将其恢复
exec 3>&1 
exec 1>test14out 
echo "This should store in the output file" 
echo "along with this line."
exec 1>&3 
echo "Now things should be back to normal"

# 3. 创建输入文件描述符
# 可以用和重定向输出文件描述符同样的办法重定向输入文件描述符。在重定向到文件之前,
# 先将STDIN文件描述符保存到另外一个文件描述符,然后在读取完文件之后再将STDIN恢复到它原来的位置
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

# 4. 创建读写文件描述符
# 尽管看起来可能会很奇怪,但是你也可以打开单个文件描述符来作为输入和输出。可以用同
# 一个文件描述符对同一个文件进行读写。
# 不过用这种方法时,你要特别小心。由于你是对同一个文件进行数据读写,shell会维护一个
# 内部指针,指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。如果不够小
# 心,它会产生一些令人瞠目的结果。看看下面这个例子, 将文件描述符 3 重定向输入输出到文件,
# 读取一行时还是正常的, 但是输出后会从第二行开头开始, 从而覆盖了原有的数据.
exec 3<>testfile
read line <&3
echo "Read: $line"
echo "This is a test line" >&3

# 5. 关闭文件描述符
# 如果你创建了新的输入或输出文件描述符,shell会在脚本退出时自动关闭它们。然而在有些
# 情况下,你需要在脚本结束前手动关闭文件描述符。要关闭文件描述符,将它重定向到特殊符号 &-
# 一旦关闭了文件描述符,就不能在脚本中向它写入任何数据,否则shell会生成错误消息。
# 在关闭文件描述符时还要注意另一件事。如果随后你在脚本中打开了同一个输出文件,shell
# 会用一个新文件来替换已有文件。这意味着如果你输出数据,它就会覆盖已有文件
exec 3> test17file 
echo "This is a test line of data" >&3 
exec 3>&-
cat test17file
exec 3> test17file
echo "This'll be bad" >&3

心得: 将某个文件描述符重定向到某个文件或文件描述符格式是

[fd1](< OR >)[&](fd2)

[ ] 代表其中内容可能忽略, 若重定向到文件描述符需多加 & 且文件描述符和 & 之间不能有空格, > 为重定向输出, < 为重定向输入, fd1 若忽略不写, 直接些后面的重定向符号的话, < 默认为STDIN, > 默认为STDOUT。比如

列出打开的文件描述符

阻止命令输出

有时候,你可能不想显示脚本的输出。这在将脚本作为后台进程运行时很常见(参见第16
章)。如果在运行在后台的脚本出现错误消息,shell会通过电子邮件将它们发给进程的属主。这
会很麻烦,尤其是当运行会生成很多烦琐的小错误的脚本时。
要解决这个问题,可以将STDERR重定向到一个叫作null文件的特殊文件。null文件跟它的名
字很像,文件里什么都没有。shell输出到null文件的任何数据都不会保存,全部都被丢掉了。
在Linux系统上null文件的标准位置是/dev/null。你重定向到该位置的任何数据都会被丢掉,
不会显示

ls -al badfile test16 2> /dev/null 
上一篇下一篇

猜你喜欢

热点阅读