Bash : IO 重定向
标准输入/输出(standard I/O)可能是软件设计原则里最重要的概念了。这个概念就是:程序应该有数据的来源端、数据的目的端(输出结果的地方)已经报告问题的地方,它们分别被称为标准输入(standard input)、标准输出(standard output)以及标准错误输出(standard error)。程序不必知道也不用关心它的输入与输出背后是什么设备,当程序运行时,这些标准 IO 就已经打开并准备就绪了。
运行时的程序称为进程,在 Linux 系统中,对于每个进程来说,始终有 3 个"文件"处于打开状态:
- stdin
- stdout
- stderr
这三个文件就是进程默认的标准输入、标准输出和标准错误输出。每个打开的文件都会被分配一个文件描述符。stdin,stdout 和 stderr 的文件描述符分别是 0,1 和 2(一个文件描述符说白了就是文件系统为了跟踪这个打开的文件而分配给它的一个数字)。对于 Bash 进程来说,stdin 默认就是键盘,stdout 和 stderr 默认都是屏幕。
我们可以把 IO 重定向简单理解为:
系统捕捉一个文件、命令、程序或脚本的输出,然后将这些输出作为输入发送到另一个文件、命令、程序或脚本中。
IO 重定向的基本概念
把标准输出重定向到文件
> 和 >> 符号把标准输出重定向到文件中
> 会覆盖掉已经存在的文件中的内容
>> 则把新的内容追加到已经存在的文件中的内容的尾部
比如下面的命令将会把文件 log.txt 变为一个空文件(就是 size 为 0):
$ : > log.txt
下面的命令则把 ls 命令输出的结果追加到 log.txt 文件的尾部:
$ ls >> log.txt
其实这是省略了标准输出的文件描述符 1 的写法,完整的写法为:
$ ls > log.txt
把标准错误输出重定向到文件
标准错误的文件描述符为 2,所以重定向标准错误到文件的写法为:
$ ls > error.txt
如果想同时把标准输出和标准错误输出重定向到同一个文件中,可以使用下面的方法:
$ ls &> log.txt
还有另外一种写法为:
$ ls >log.txt >&
重定向到另一个文件描述符
比如通过这种方式把标准输出和标准错误输出重定向到同一个文件中:
$ ls >log.txt >&
注意:& 符号后面跟的是文件描述符而不是文件。
总结一下,我们大概可以得到下面两条规则:
M>N
# "M" 是一个文件描述符,如果没有明确指定的话默认为 1。
# "N" 是一个文件名。
# 文件描述符 "M" 被重定向到文件 "N"。
M>&N
# "M" 是一个文件描述符,如果没有明确指定的话默认为 1。
# "N" 是另一个文件描述符。
关闭文件描述符
文件描述符是可以关闭的,典型的写法有下面几种:
n<&- # 关闭输入文件描述符 n
0<&-, <&- # 关闭 stdin
n>&- # 关闭输出文件描述符 n
1>&-, >&- # 关闭 stdout
>/dev/null 2>&1 和 2>&1 >/dev/null
Linux 系统中有一个特殊的设备文件 /dev/null,发送给它的任何内容都会被丢弃,因此如果需要丢弃标准输出和标准错误输出的内容时可以使用下面的方法:
1>/dev/null 2>/dev/null
下面的写法与上面的写法是等同的,接下来我们重点来解释下面的写法:
>/dev/null 2>&1
>/dev/null
>/dev/null 的作用是将标准输出 1 重定向到 /dev/null 中,因此标准输出中的内容被完全丢弃了。
2>&1
2>&1 用到了重定向绑定,采用 & 可以将两个输出绑定在一起,也就是说错误输出将会和标准输出输出到同一个地方。
其实 Linux 在执行 shell 命令之前,就会确定好所有的输入输出位置,解释顺序为从左到右依次执行重定向操作。所以 >/dev/null 2>&1 的作用就是让标准输出重定向到 /dev/null 中,接下来因为错误输出的文件描述符 2 被重定向绑定到了标准输出的描述符 1,所以错误输出也被定向到了 /dev/null 中,错误输出同样也被丢弃了。
2>&1 >/dev/null
2>&1 >/dev/null 执行的结果和 >/dev/null 2>&1 是不一样的!它的执行过程为:
- 2>&1,将错误输出绑定到标准输出上。由于此时的标准输出是默认值,也就是输出到屏幕,所以错误输出会输出到屏幕。
- >/dev/null,将标准输出1重定向到 /dev/null 中。
exec 与重定向
exec <filename 命令会将 stdin 重定向到文件中。 从这句开始, 所有的 stdin 就都来自于这个文件了, 而不是标准输入(通常都是键盘输入)。 这样就提供了一种按行读取文件的方法。当然,也可以用这种方法来重定向标准输出和标准错误输出。下面的 upper.sh 程序把输入文件中的字母转换为大写并输出到另外一个文件中,脚本中使用 exec 同时重定向了 stdin 和 stdout:
#!/bin/bash E_FILE_ACCESS=
E_WRONG_ARGS= if [ ! -r "$1" ] # 判断指定的输入文件是否可读
then
echo "Can't read from input file!"
echo "Usage: $0 input-file output-file"
exit $E_FILE_ACCESS
fi # 即使输入文件($)没被指定 if [ -z "$2" ]
then
echo "Need to specify output file."
echo "Usage: $0 input-file output-file"
exit $E_WRONG_ARGS
fi exec <& # 保存默认 stdin
exec < $ # 将会从输入文件中读取. exec >& # 保存默认 stdout
exec > $ # 将写到输出文件中.
# 假设输出文件是可写的 # -----------------------------------------------
cat - | tr a-z A-Z # 转换为大写
# 从 stdin 中读取
# 写到 stdout 上
# 然而,stdin 和 stdout 都被重定向了
# -----------------------------------------------
exec >& >&- # 恢复 stout
exec <& <&- # 恢复 stdin # 恢复之后,下边这行代码将会如预期的一样打印到 stdout 上
echo "File \"$1\" written to \"$2\" as uppercase conversion."
exit
在 upper.sh 程序所在的目录下创建 in.txt 和 out.txt 文件,编辑 in.txt 文件的内容为:
abcd
xxyy
然后运行 upper.sh 程序:
$ ./upper.sh in.txt out.txt

其实还有一种稍微简单一些的方式把标准输入和标准输出重定向到文件:
[j]<>myfile
# 为了读写 myfile,把文件 myfile 打开, 并且将文件描述符 j 分配给它。
# 如果文件 myfile 不存在, 那么就创建它。
# 如果文件描述符 j 没指定,那默认是 fd 0,即标准输入。
这种应用通常是为了把内容写到一个文件中指定的地方,比如下面的 demo:
echo > myfile # 写字符串到 myfile
exec <> myfile # 打开 myfile 并且将 fd 分配给它
read -n <& # 只读取4个字符
echo -n . >& # 写一个小数点
exec >&- # 关闭 fd
cat myfile # ==> 1234.67890
避免子 shell
默认情况下,不能在子 shell 中改变父 shell 中变量的值。下面的 demo 通过 exec 重定向 IO 有效的规避了子 shell 问题:
#!/bin/bash E_WRONG_ARGS=
if [ -z "$1" ]
then
echo "Usage: $0 input-file"
exit $E_WRONG_ARGS
fi Lines= cat "$1" | while read line; # 管道会产生子 shell
do {
echo $line
(( Lines++ )); # 增加这个变量的值
# 但是外部循环却不能访问
}
done echo "Number of lines read = $Lines" #
# 错误!
echo "------------------------"
exec <> "$1"
while read line <&
do {
echo "$line"
(( Lines++ )); # 增加这个变量的值
# 现在外部循环就可以访问了
# 没有子shell, 现在就没问题了
}
done
exec >&-
echo "Number of lines read = $Lines"
exit
把上面的代码保存到 avoid-subshell.sh 文件中,然后运行下面的命令:
$ ./avoid-subshell.sh in.txt

使用重定向 IO 的方式获得了正确的结果。
代码块重定向
像 while、until、for 和 if/then 等代码块也是可以进行 IO 重定向的,下面的 demo 把 while 循环的标准输入重定向到一个文件:
#!/bin/bash E_WRONG_ARGS=
if [ -z "$1" ]
then
echo "Usage: $0 input-file"
exit $E_WRONG_ARGS
fi count= while [ "$name" != xxyy ]
do
read name
if [ -z "$name" ]; then
break
fi
echo $name
let "count += 1"
done <"$1"
echo "$count names read"
exit
把上面的代码保存到文件 whileblock.sh 中,然后执行下面的命令,可以看到标准输入重定向后的结果:
$ ./whileblock.sh in.txt

下面的 demo 则同时重定向了 for 循环的标准输入和标准输出:
#!/bin/bash E_WRONG_ARGS=
if [ -z "$1" ]
then
echo "Usage: $0 input-file output-file"
exit $E_WRONG_ARGS
fi if [ -z "$2" ]
then
echo "Usage: $0 input-file output-file"
exit $E_WRONG_ARGS
fi FinalName="xxyy"
line_count=$(wc "$1" | awk '{ print $1 }') for name in $(seq $line_count)
do
read name
echo "$name"
if [ "$name" = "$FinalName" ]
then
break
fi
done < "$1" > "$2"
exit
把上面的代码保存到文件 forblock.sh 中,然后执行下面的命令,终端上没有任何输出,因为输出都被重定向到了 out.txt 文件:
$ ./forblock.sh in.txt out.txt
总结
在众多的场景中,IO 重定向的结果是显而易见的。但在一些特殊的用例中结果就不是那么明显了,需要我们理解重定向的基本规则,才能理解哪些特殊写法的含义或是写出我们自己满意的脚本。
参考:
《高级 Bash 脚本编程》
shell 中的>/dev/null 2>&1 是什么鬼?
Bash : IO 重定向的更多相关文章
- Perl IO:IO重定向
文件句柄和文件描述符的关系 文件描述符是操作系统的资源,对于实体文件来说,每打开一次文件,操作系统都会为该进程分配一个文件描述符来关联(指向)这个文件,以后操作文件数据都根据这个文件描述符来操作,而不 ...
- linux学习14 Linux运维高级系统应用-glob通配及IO重定向
一.回顾 1.bash基础特性:命令补全,路径补全,命令引用 2.文件或目录的复制,移动及删除操作 3.变量:变量类型 存储格式,数据表示范围,参与运算 二.bash的基础特性 1.globbing: ...
- 四、IO重定向和管道以及基本文本处理工具
一.三种IO设备 程序:数据+指令 或 数据结构+算法程序必须能够读入输入然后经过加工来产生结果,其接受的输入可以是变量.数组.列表.文件等等,生产出来的结果可以使变量.数组.列表.文件等等.即:程序 ...
- shell IO重定向
I/O重定向 默认情况下,有3个"文件"处于打开状态,stdin,stdout,stderr:重定向的解释:捕捉一个文件,命令,程序,脚本或者脚本中的代码块的输出,然后将这些输出作 ...
- [C++]竞赛模板·数据统计与IO(重定向版与非重定向版)
/* 数据统计与IO 重定向版模板 描述:本机测试用文件数据流重定向,一旦提交到比赛就自动“删除”重定向语句 */ # define LOCAL #include<stdio.h> # ...
- IO重定向与管道
一.三种IO设备 程序:数据+指令 或 数据结构+算法 程序必须能够读入输入然后经过加工来产生结果,其接受的输入可以是变量.数组.列表.文件等等,生产出来的结果可以使变量.数组.列表.文件等等.即: ...
- 4-3 管理及IO重定向
1. 系统设定默认输出设备:标准输出(STDOUT,1) 系统设定默认输入设备:标准输入(STDIN,0) 系统设定默认错误设备:标准错误(STDERR,2) 2. 标准输入:键盘 标准输出和错误输出 ...
- <实训|第十一天>学习一下linux中的进程,文件查找,文件压缩与IO重定向
[root@localhost~]#序言 在今后的工作中,运维工程师每天的例行事务就是使用free -m,top,uptime,df -h...每天都要检查一下服务器,看看是否出现异常.那么今天我们就 ...
- bash shell:重定向标准错误输出
如何重定向标准错误输出到标准输出?如何把标准错误输出输出到一个文件? Bash提供了I/O重定向工具,有3个缺省的文件(标准输出流): stdin - 用来获取输入,比如键盘.文件重定向 stdout ...
随机推荐
- [20171120]关于INBOUND_CONNECT_TIMEOUT设置.txt
[20171120]关于INBOUND_CONNECT_TIMEOUT设置.txt --//上午翻看以前我的发的帖子,发现链接:http://www.itpub.net/thread-2066758- ...
- python UDP套接字通信
UDPserver.py import socket #导入套接字模块 s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # - socket.A ...
- 7z常用命令行&7z检测压缩包完整性&7z压缩包错误不执行rsync同步
7Z简介&常用命令 7Z脚本使用说明 7Z检测压缩包完整性脚本 7Z压缩包错误不执行Rsync脚本 1.7Z简介&常用命令 ⑴简介: 7z,全称7-Zip, 是一款开源软件.是目前公认 ...
- Linux 小知识翻译 - 「架构 续」(arch)
上次,从「计算机的内部构造」的角度解释了架构这个术语.这次,介绍下架构中经常提到的「i386架构」及之后的「i486」,「i586」. 安装Linux的时候,很多人即使不了解但也会经常听到i386架构 ...
- 【Beta Scrum】冲刺!5/5
1. 今日完成情况 人员 学号 分工 是否完成 完成情况 胡武成 031502610 解决短信内容,辅助web端解决在线编辑 Y 短信已解决,在线编辑已有解决方案 郭剑南 031502609 修改we ...
- SAP CRM 自定义控制器与数据绑定
当用户从视图离开时,视图将失去它的数据.解决这个问题,需要引入自定义控制器(Custom Controller)(译者注:SAP CRM自定义端中,不同地方的Custom Controller会翻译为 ...
- Activiti工作流的定义部署和执行
工作流引擎 个人觉得直接理解工作流引擎概念有点难度,我们可以先通过了解工作流引擎的职责再反过来理解工作流引擎,工作流引擎一般都做两件事情: 1.定义流程,也就是给我们提供某种规范来定义规则,以 ...
- CF1045G:AI robots(CDQ分治)
Description 火星上有$n$个机器人排成一行,第$i$个机器人的位置为$x_i$,视野为$r_i$,智商为$q_i$.我们认为第$i$个机器人可以看到的位置是$[x_i−r_i,x_i+ ...
- 怎样使用CSS3媒体查询(Media Queries)制作响应式网站
自本周开始博主将开始同大家一起研究响应式web设计,CSS3 Media Queries是入门,本周更新,博主将给大家分享media queries的一些常用的用法及注意事项. Media Queri ...
- go标准库的学习-regexp
参考:https://studygolang.com/pkgdoc 导入方式: import "regexp" regexp包实现了正则表达式搜索. 正则表达式采用RE2语法(除了 ...