高级shell编程笔记(第十六章 I/O重定向)

第十六章 I/O重定向

默认情况下始终有 3 个"文件"处于打开状态,stdin (键盘)、stdout (屏幕) 和 stderr (错误消息输出到屏幕上)。这 3 个文件和其他打开的文件都可以被重定向。
对于重定向简单的解释就是捕捉一个文件、命令、程序、脚本或者是脚本中的代码块(参见 Example 3-1 和 Example 3-2)的输出,然后将这些输出作为输入发送到另一个文件、命令、程序或脚本中。

每个打开的文件都会被分配一个文件描述符。stdin、stdout 和 stderr 的文件描述符分别是 0、1 和 2。对于正在打开的额外文件,保留了描述符 3 到 9。在某些时候将这些格外的文件描述符分配给 stdin、stdout 或者是 stderr 作为临时的副本链接是非常有用的。在经过复杂的重定向和刷新之后需要把它们恢复成正常的样子 (参见 Example 16-1)。

COMMAND_OUTPUT >   #重定向 stdout 到一个文件.如果没有这个文件就创建, 否则就覆盖.

ls -lR > dir-tree.list   #创建一个包含目录树列表的文件.

: > filename   # > 会把文件"filename"截断为 0 长度
#如果文件不存在, 那么就创建一个 0 长度的文件(与'touch'的效果相同).
# : 是一个占位符, 不产生任何输出.

> filename   #与上边的": >"效果相同, 但是在某些 shell 下可能不能工作.

COMMAND_OUTPUT >>   #重定向 stdout 到一个文件.如果文件不存在, 那么就创建它, 如果存在, 那么就追加到文件后边.
1>filename   #重定向 stdout 到文件"filename".
1>>filename   #重定向并追加 stdout 到文件"filename".
2>filename   #重定向 stderr 到文件"filename".
2>>filename   #重定向并追加 stderr 到文件"filename".
&>filename   # 将 stdout 和 stderr 都重定向到文件"filename".
2>&1   #重定向 stderr 到 stdout.得到的错误消息与 stdout 一样, 发送到一个地方.
i>&j   #重定向文件描述符 i 到 j.指向 i 文件的所有输出都发送到 j 中去.
>&j   #默认的, 重定向文件描述符 1(stdout)到 j.所有传递到 stdout 的输出都送到 j 中去.
0 < FILENAME   #从文件中接受输入.与">"是成对命令, 并且通常都是结合使用.

[j]<>filename   #为了读写"filename", 把文件"filename"打开, 并且分配文件描述符"j"给它.
#如果文件"filename"不存在, 那么就创建它.
#如果文件描述符"j"没指定, 那默认是 fd 0, stdin.

#这种应用通常是为了写到一个文件中指定的地方.
echo 1234567890 > File   #写字符串到"File"
exec 3<>File   #打开"File"并且给他分配 fd 3.
read -n 4 <&3   #只读4个字符
echo -n . >&3   #写入一个小数点
exec 3>&-   #关闭fd 3
cat File   1234.67890

可以将多个输出流量重定向到一个文件上。

ls -yz >> command.log 2>&1   #将错误选项"yz"的结果放到文件"command.log"中.
注意:下面这个例子就不会给出相同的结果
ls -yz 2>&1 >> command.log   #输出一个错误消息, 但是并不写到文件中.
#如果将stdout和stderr都重定向,命令得顺序会有些不同。

关闭文件描述符

n<&-   #关闭输入文件描述符n
0<&-,<&-   #关闭stdin

n>&-   #关闭输出文件描述符n
1>&-,>&-   #关闭stdout

子进程继承了打开的文件描述符。这就是为什么管道可以工作。果想阻止 fd 被继承,那么可以关掉它。

#只重定向stderr到一个管道
exec 3>&1   #保存当前stdout的值
ls -l 2>&1 >&3 3>&- | grep bad 3>&-   # 对'grep'关闭 fd 3(但不关闭'ls').
exec 3>&-   #现在对于剩余的脚本关闭它.

如果想了解关于 I/O 重定向更多的细节参见附录 E。

16.1 使用exec

exec < filename 命令会将stdin重定向到文件中。从这句开始,后边的输入就都来自于这个文件了,而不是标准输入了。这样就提供了一种按行读取文件的方法,并且可以使用sed和awk来对每一行进行分析。

Example 16-1 使用exec重定向标准输入

#!/bin/bash
#
exec 6<&0   #将文件描述符 #6 与 stdin 链接起来.保存了 stdin.
exec < data-file   # stdin 被文件"data-file"所代替.

read a1   # 读取文件"data-file"的第一行.
read a2   # 读取文件"data-file"的第二行.
echo
echo "Following lines read from file."
echo "------------------------------"
echo $a1
echo $a2
echo;echo;echo
exec 0<&6 6<&-   #现在将 stdin 从 fd #6 中恢复, 因为刚才我们把 stdin 重定向到#6 了,
#然后关闭 fd #6 ( 6<&- ), 好让这个描述符继续被其他进程所使用.

echo -n "Enter data"
read b1   # 现在"read"已经恢复正常了, 就是从 stdin 中读取.
echo "Input read from stdin"
echo "---------------------"
echo "b1 = $b1"
echo
exit 0

同样的 exec > filename 命令将会把 stdout 重定向到一个指定的文件中。这样所有的命令输出就都会发向那个指定的文件,而不是 stdout。

Example 16-2 使用exec来重定向stdout

#!/bin/bash
#
LOGFILE=logfile.txt
exec 6>&1   # 将 fd #6 与 stdout 相连接.保存 stdout.
exec > $LOGFILE   # stdout 就被文件"logfile.txt"所代替了.

echo -n "Logfile: "
date
echo "----------------------------"
echo

echo "Output of \"ls -al\" command"
echo
ls -al
echo;echo
echo "Output of \"df\" command"
echo
df

exec 1>&6 6>&-   # 恢复 stdout, 然后关闭文件描述符#6.
echo
echo "== stdout now restored to default =="
echo
ls -al
echo
exit 0

Example 16-3 使用exec在同一脚本中重定向stdin和stdout

#!/bin/bash
#
#将一个指定的输入文件转换为大写。
E_FILE_ACCESS=70
E_WRONG_ARGS=71

if [ ! -r "$1" ];then   # 判断指定的输入文件是否可读
  echo "无法读取输入文件!"
  echo "用法:$0 输入文件 输出文件"
  exit $E_FILE_ACCESS
fi

if [ -z "$2" ];then
  echo "需要指定输出文件."
  echo "用法:$0 输入文件 输出文件"
  exit $E_WRONG_ARGS
fi

exec 4<&0
exec < $1   # 将会从输入文件中读取.
exec 7<&1
exec > $2   # 将写到输出文件中.假设输出文件是可写的.

cat - |tr a-z A-Z

exec 1>&7 7>&-   # 恢复 stout.
exec 0<&4 4<&-   # 恢复 stdin.
# 恢复之后, 下边这行代码将会如期望的一样打印到 stdout 上.
echo "File \"$1\" written to \"$2\" as uppercase conversion."

exit 0

I/O重定向是一种避免可怕的子shell中不可存取变量问题的方法。

Example 16-4 避免子shell

#!/bin/bash
#
Lines=0
echo
cat myfile.txt | while read line;
do
    echo $line
    (( Lines++ ))   #增加这个变量的值,但是外部循环却不能存取。子shell问题。
done

echo "Number of lines read = $Lines"
echo "------------------------"

exec 3<> myfile.txt
while read line <&3
do
    echo "$line"
    (( Lines++))
done
exec 3>&-
echo "Number of lines read = $Lines"
echo
exit 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值