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. 对象存储在什么地方(java编程思想)

    用引用操作对象.创建了一个引用,需要进行初始化(与事物进行关联),才能正常使用.new将引用于对象进行关联 对象存储到什么地方? 程序运行时,对象是怎么进行放置安排的呢?特别是内存是怎么分配的呢?对这 ...

  2. Python数据类型-04.字典

    字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据 ------------ 完美的分割线 ------------- 1.字典引入 # 为何还要用字典?存放一个人的信 ...

  3. 把字符串中的空格替换为"%20"

    这个需要注意的是字符串的结尾最后一个字符为'\0',并不是空字符,复制时要一块复制,算法思想就是先计算出字符串中总的空格数,然后 重新计算字符串的长度,由于"%20"为3个字符,比 ...

  4. 高性能Mysql学习笔记之事务

    传送门 什么是事务? 事务就是一组原子性的SQL查询,或者说一个独立的工作单元.如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就会执行该组查询.如果其中有任何一条语句因为崩溃或其他原因无 ...

  5. Jenkins搭建.NET自动编译发布远程环境

    继上一篇文章Jenkins搭建.NET自动编译发布本地环境 发布到本地成功后,接下来配置发布到远程环境. Build配置——发布到远程 根据前面VS中发布项目,生成的CustomProfile2 来配 ...

  6. HDU1735 字数统计

    版权声明:长风原创 https://blog.csdn.net/u012846486/article/details/28011667 字数统计 Time Limit: 1000/2000 MS (J ...

  7. Spring Cloud 入门 之 Eureka 篇(一)

    原文地址:Spring Cloud 入门 之 Eureka 篇(一) 博客地址:http://www.extlight.com 一.前言 Spring Cloud 是一系列框架的有序集合.它利用 Sp ...

  8. Remi 安装源

    Remi repository 是包含最新版本 PHP 和 MySQL 包的 Linux 源,由 Remi 提供维护.有个这个源之后,使用 YUM 安装或更新 PHP.MySQL.phpMyAdmin ...

  9. 汇编_指令_JMP

    JMP指令 JMP是汇编语言中的无条件跳转指令.无条件跳转指令可转到内存中任何程序段.转移地址可在指令中给出,也可以在寄存器中给出,或在储存器中指出. 中文名:无条件跳转指令 外文名:JMP 和调用指 ...

  10. py基础4--迭代器、装饰器、软件开发规范

    本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1. 列表生成式,迭代器&生成器 列表生成式 我现在有个需求, ...