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. Jenkins插件开发(二)-- HelloWorld

    在上一篇blog中我们讲了如何搭建jenkins插件的开发环境,接下来介绍如何开发我们的插件. 创建HelloWorld插件 学习每门新语言的时候,我们都会写一个HelloWorld程序,这里介绍的是 ...

  2. window和Linux下Redis的安装及运行

    Window篇  Redis的官方目前公开的版本分为三个类别Stable.Beta和Unstable.这些版本一般只是针对Linux.Unix内核的系统,虽然官方的不支持Window系统,但是微软开源 ...

  3. .net 应用程序 发布上线注意事项

    生产环境发布时,对应的程序目录必须新建当日rar压缩包进行备份生产环境数据库发布时,必须创建存储过程的副本sql用于回滚,操作方式:F7调出对象资源管理器详细信息->选中所有存储过程->编 ...

  4. dir listing 目录文件列表索引

    一般而言,网站应用都有一个入口,比如说:index.php,index.html,app.js等.通过这个路口,以及相应的路由功能,去到网站各个功能版块. 而网站的目录结构,目录里面的文件列表,一般都 ...

  5. Linux运维学习笔记-文件系统知识体系总结

    文件系统知识总结 新买的硬盘要存放数据需要怎么做? 首先将硬盘装机做RAID,做完RAID后进行分区,分完区后格式化创建文件系统,最后存放数据. 硬盘的内外部结构: 物理形状: 接口类型: IDE(I ...

  6. linux 模拟生成 CAN 设备

    /************************************************************************************** * linux 模拟生成 ...

  7. leisure time

    终于把论文翻译完了,天哪,现在感觉解脱一般. 这些天看电视,玩游戏,也不止学了写什么,现在调整了下心情,重新确定下目标吧. 最近很想学Python和Qt,哎,技术永远都是学不完了,理解操作系统和组成原 ...

  8. test20181024 hao

    题意 分析 考场10分 直接\(O(nm)\)模拟即可. #include<cstdlib> #include<cstdio> #include<cmath> #i ...

  9. 为已编译的DLL附带强命名

    在我们开发的过程中,会经常调用其他人写好的DLL类库,由于种种的原因,不管是公司规定,还是个人习惯等等的原因,有时候需要调用各个类库直接邀请必须强命名. 但是我们临时也无法找到源代码进行重新编译等事情 ...

  10. 关于location.href赋值的php用法

    <?php echo $_GET['action']; ?> <!doctype html> <html lang="zh"> <head ...