I/O重定向

简述:

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

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

文件重定向通常的用法:

       COMMAND_OUTPUT >
# 重定向stdout到一个文件.
# 如果没有这个文件就创建, 否则就覆盖. ls -lR > dir-tree.list
# 创建一个包含目录树列表的文件. : > filename
# > 会把文件"filename"截断为0长度.
# 如果文件不存在, 那么就创建一个0长度的文件(与'touch'的效果相同).
# : 是一个占位符, 不产生任何输出. > filename
# > 会把文件"filename"截断为0长度.
# 如果文件不存在, 那么就创建一个0长度的文件(与'touch'的效果相同).
# (与上边的": >"效果相同, 但是在某些shell下可能不能工作.) COMMAND_OUTPUT >>
# 重定向stdout到一个文件.
# 如果文件不存在, 那么就创建它, 如果存在, 那么就追加到文件后边. # 单行重定向命令(只会影响它们所在的行):
# -------------------------------------------------------------------- >filename
# 重定向stdout到文件"filename".
>>filename
# 重定向并追加stdout到文件"filename".
>filename
# 重定向stderr到文件"filename".
>>filename
# 重定向并追加stderr到文件"filename".
&>filename
# 将stdout和stderr都重定向到文件"filename". #==============================================================================
# 重定向stdout, 一次一行.
LOGFILE=script.log echo "This statement is sent to the log file, \"$LOGFILE\"." >$LOGFILE
echo "This statement is appended to \"$LOGFILE\"." >>$LOGFILE
echo "This statement is also appended to \"$LOGFILE\"." >>$LOGFILE
echo "This statement is echoed to stdout, and will not appear in \"$LOGFILE\"."
# 每行过后, 这些重定向命令会自动"reset". # 重定向stderr, 一次一行.
ERRORFILE=script.errors bad_command1 >$ERRORFILE # 错误消息发到$ERRORFILE中.
bad_command2 >>$ERRORFILE # 错误消息添加到$ERRORFILE中.
bad_command3 # 错误消息echo到stderr,
#+ 并且不出现在$ERRORFILE中.
# 每行过后, 这些重定向命令也会自动"reset".
#============================================================================== >&
# 重定向stderr到stdout.
# 得到的错误消息与stdout一样, 发送到一个地方. i>&j
# 重定向文件描述符i 到 j.
# 指向i文件的所有输出都发送到j中去. >&j
# 默认的, 重定向文件描述符1(stdout)到 j.
# 所有传递到stdout的输出都送到j中去. < FILENAME
< FILENAME
# 从文件中接受输入.
# 与">"是成对命令, 并且通常都是结合使用.
#
# grep search-word <filename [j]<>filename
# 为了读写"filename", 把文件"filename"打开, 并且分配文件描述符"j"给它.
# 如果文件"filename"不存在, 那么就创建它.
# 如果文件描述符"j"没指定, 那默认是fd , stdin.
#
# 这种应用通常是为了写到一个文件中指定的地方.
echo > File # 写字符串到"File".
exec <> File # 打开"File"并且给它分配fd .
read -n <& # 只读4个字符.
echo -n . >& # 写一个小数点.
exec >&- # 关闭fd .
cat File # ==> 1234.67890
# 随机存储. |
# 管道.
# 通用目的的处理和命令链工具.
# 与">"很相似, 但是实际上更通用.
# 对于想将命令, 脚本, 文件和程序串连起来的时候很有用.
cat *.txt | sort | uniq > result-file
# 对所有的.txt文件的输出进行排序, 并且删除重复行,
# 最后将结果保存到"result-file"中.

将输入输出重定向与管道相结合

    command < input-file > output-file

    command1 | command2 | command3 > output-file

将多个输出流重定向到一个文件上:

    ls -yz >> command.log >&
# 将错误选项"yz"的结果放到文件"command.log"中.
# 因为stderr被重定向到这个文件中,
#+ 所有的错误消息也就都指向那里了. # 注意, 下边这个例子就不会给出相同的结果.
ls -yz >& >> command.log
# 输出一个错误消息, 但是并不写到文件中. # 如果将stdout和stderr都重定向,
#+ 命令的顺序会有些不同.

关闭文件描述符

n<&-
#关闭输入文件描述符n. <&-
<&-
#关闭stdin. n>&-
#关闭输出文件描述符n. >&-
>&-
#关闭stdout.

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

    # 只重定向stderr到一个管道.

    exec >&                              # 保存当前stdout的"值".
ls -l >& >& >&- | grep bad >&- # 对'grep'关闭fd (但不关闭'ls').
# ^^^^ ^^^^
exec >&- # 现在对于剩余的脚本关闭它.

exec

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

    #!/bin/bash
# 使用'exec'重定向标准输入. exec <& # 将文件描述符#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 <& <&-
# 现在将stdin从fd #6中恢复, 因为刚才我们把stdin重定向到#6了,
#+ 然后关闭fd # ( <&- ), 好让这个描述符继续被其他进程所使用.
#
# <& <&- 这么做也可以. echo -n "Enter data "
read b1 # 现在"read"已经恢复正常了, 就是从stdin中读取.
echo "Input read from stdin."
echo "----------------------"
echo "b1 = $b1" echo exit

例如可以这样读文件:

exec <>test.sh;
#打开test.sh可读写操作,与文件描述符3绑定 while read line<&
do
echo $line;
done
#循环读取文件描述符3(读取的是test.sh内容)
exec >&-
exec <&-
#关闭文件的,输入,输出绑定

当然一般我们可以直接重定向文件到一个代码块,相当于重定向到这个代码块的标准输入。

重定向到代码块

通常我们可以将利用将代码块的输入输出重定向的方法实现一些对于文件的操作。

例如:

# while的重定向
while [ "$name" != Smith ]
do
read name
echo $name
done <"$Filename" # 以上结构也可以这样写
exec <& # 把标准输入绑定到文件描述符3,以便稍后恢复
exec <"$Filename" # 重定向标准输入为这个文件 while [ "$name" != Smith ]
do
read name
echo $name
done exec <& # 恢复标准输入
exec <&- # 关闭文件描述符3以供后续使用 # until的重定向
until [ "$name" = Smith ]
do
read name
echo "$name"
done <"$Filename" # for循环的重定向
lineCount=`wc $Filename | awk '{print $1}'` # 统计文件的行数 for name in `seq $lineCount`
do
read name
echo $name
if [ "$name" = Smith ] # 循环到Smith的时候就退出循环
then
break
fi
done <"$Filename" # if/then结构的重定向
if :
then
read name
echo "$name"
fi <"$Filename" # 这个重定向只读了文件中的一行

Here Documents

here documents是一段代码块,可以利用重定向将这个代码块传递到一个交互或者命令中。

标准的结构为:

COMMAND <<HERESTRING
...
HERESTRING

例如:

tail << HERE
a
b
HERE

输出为:

a
b

也可以用<<-来排除tab对heredocuments的影响

例如:

cat <<- HERE
a
b
HERE

输出会是

a
b

此外,here document可以使用参数替换

例如:

name="Smith"
cat << HERE
a
$name
HERE

将会输出

a
Smith

参数替换可以使用''来禁用

name="Smith"
cat << 'HERE'
a
$Smith
HERE

将会输出

a
$Smith

可以利用这种方法来产生代码,不会造成问题。

函数也可以使用here document来提供数据

function echoabc(){
read line1
echo line1
read line2
echo line2
} echoabc << HERE
a
b
HERE

也可以输出

a
b

还可以使用here document注释代码块

例如:

: << HERE
echo a
HERE echo b

输出为:

b

相当于将here document传递给了:,不会有任何的影响。

注意here document的结束符前后都不可以有空格,否则会产生识别上的错误

here string

here string可以认为是here document的一种形式。

例如:

HERE="abcd"
tail <<< $HERE

将会输出:

abcd

相当于直接将一个string传递给了命令.

一个小陷阱:

echo "some text here" > file
cat < file > file
cat < file
sed 's/hello/hi/' file > file

上面这两个例子都会清空文件file。原因同一个:在 IO Redirection 中,stdout 与 stderr 的管道会先准备好,才会从 stdin 取资料。故而上面的两个例子都会先执行"> file"操作,即清空了文件,再执行前面的操作。这个陷阱需要额外注意。

参考资料:

https://www.jianshu.com/p/681a3a762fe5

http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=218853&page=7#pid1636825

linux shell脚本编程笔记(五): 重定向的更多相关文章

  1. linux shell脚本编程笔记(二): 分支结构

    1.if if command then commands fi if command then commands else commands fi if command1 then command ...

  2. linux shell脚本编程笔记(一): 构建基本脚本

    1. echo -n str        打印不换行 2. 反引号来圈住命令传入变量 eg: 生成日志文件: #!/bin/bash today=`date +%y%m%d` ls /usr/bin ...

  3. linux shell脚本编程笔记(四): 获取字符串长度的七种方法

    获取字符串长度的七种方法 1. \${#str} 2.awk的length 备注:1) 最好用{}来放置变量2) 也可以用length($0)来统计文件中每行的长度 3.awk的NF 备注: -F为分 ...

  4. linux shell脚本编程笔记(三): 三种引号的区别

    双引号.单引号.反引号的区别 测试用例: OPDATE=`date -d '-1 day' +%Y%m%d` ) do FILEDATE=`date -d "-$i day" +% ...

  5. Linux shell脚本编程(三)

    Linux shell脚本编程 流程控制: 循环语句:for,while,until while循环: while CONDITION; do 循环体 done 进入条件:当CONDITION为“真” ...

  6. Linux shell脚本编程(二)

    Linux shell脚本编程(二) 练习:求100以内所有偶数之和; 使用至少三种方法实现; 示例1: #!/bin/bash # declare -i sum=0 #声明一个变量求和,初始值为0 ...

  7. Linux shell脚本编程(一)

    Linux shell脚本编程: 守护进程,服务进程:启动?开机时自动启动: 交互式进程:shell应用程序 广义:GUI,CLI GUI: CLI: 词法分析:命令,选项,参数 内建命令: 外部命令 ...

  8. Linux Shell脚本编程--Linux特殊符号大全

    Linux Shell脚本编程--Linux特殊符号大全 linux_shell 特殊符号的介绍 2011

  9. Linux Shell脚本编程while语句

    Linux Shell脚本编程while语句案例 1,每隔3秒,打印一次系统负载 #!/bin/bash while truedo    uptime    sleep 3done 2,把监控结果保存 ...

随机推荐

  1. 流程设计器jQuery + svg/vml(Demo5 - 撤消与重做)

    上篇完成了画线,接下来是撤消与重做. 代码:GoFlow_05.zip 演示地址:Demo 微信演示公众号: 另:Silverlight版 Silverlight版Demo

  2. day02Java基础学习笔记

    自动类型转换:容量小的类型自动转换为容量大的数据类型.数据类型按容量大小排序为: byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型 1.各种进制的表现形式和特点 二 ...

  3. 关于React setState的实现原理(一)

    前言 首先在学习react的时候就对setSate的实现有比较浓厚的兴趣,那么对于下边的代码,可以快速回答吗? class Root extends React.Component { constru ...

  4. 使用Lucene对doc、docx、pdf、txt文档进行全文检索功能的实现

    转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/76273859 本文出自[我是干勾鱼的博客] 这里讲一下使用Lucene对doc. ...

  5. mysql下,保存时间时具体时间丢失,只保存了日期的问题

    将日志信息记入数据库时增加了一个时间字段,发现存入数据库时只保留了日期,而没有时分秒信息. 我这边环境是(SRPINGMVC+Mybatis,mysql版本5.6.28以上),java层使用类型为ja ...

  6. 关于const 和指针

    这个很久之前就很困扰的问题,现在再理一下: 1,指向const对象的指针 >C++强制要求指向const对象的指针也必须具有const特性!!!也就是不能把一个const对象的地址赋给一个非co ...

  7. v4l2框架函数调用关系

    所有的设备节点和子设备节点都是通过__video_register_device()注册的 1.对于video设备节点: 用户空间ioctl(VIDIOC_S_FMT)---> v4l2_fop ...

  8. H3C 交换机基本设置(telnet、SSH、链路聚合)

    http://www.h3c.com/cn/d_201710/1038172_30005_0.htm#_Toc493869056 H3C S5560S-SI&S5130S-SI[LI]& ...

  9. postgresql双机热备、高可用方案(采用pacemaker+corosync实现)

    http://blog.csdn.net/qguanri/article/details/51151974 需求描述 我们有两台centos7的数据库主机A.B.要对A.B实现双机热备,A作为数据库m ...

  10. docker基于commit命令创建支持ssh服务的镜像

    以centos为基础,目的使用ssh服务远程连接docker容器. 环境:宿主机centos7(宿主机ip地址为192.168.164.130),直接搜索docker的centos镜像,下载最新版本. ...