分享

I/O重定向 详解及例子

 weicat 2007-08-01

1、基本概念
a
I/O重定向通常与 FD有关,shellFD通常为10个,即 09
b
、常用FD3个,为0stdin,标准输入)、1stdout,标准输出)、2stderr
标准错误输出),默认与keyboardmonitormonitor有关;
c
、用 < 来改变读进的数据信道(stdin),使之从指定的档案读进;
d
、用 > 来改变送出的数据信道(stdout, stderr),使之输出到指定的档案;
e
0 < 的默认值,因此 < 0<是一样的;同理,> 1> 是一样的;
f
、在IO重定向 中,stdout stderr 的管道会先准备好,才会从 stdin 读进资料;
g
、管道“|”(pipe line):上一个命令的 stdout 接到下一个命令的 stdin;
h
tee 命令是在不影响原本 I/O 的情况下,将 stdout 复制一份到档案去;
i
bashksh)执行命令的过程:分析命令-变量求值-命令替代(``$( ))-重
定向-通配符展开-确定路径-执行命令;
j
( ) command group 置于 sub-shell 去执行,也称 nested sub-shell,它有
一点非常重要的特性是:继承父shellStandard input, output, and error plus any
other open file descriptors

k
exec 命令:常用来替代当前 shell 并重新启动一个 shell,换句话说,并没有启
动子 shell。使用这一命令时任何现有环境都将会被清除,。exec 在对文件描述符进行
操作的时候,也只有在这时,exec 不会覆盖你当前的 shell 环境。


2
、基本IO
cmd > file
stdout 重定向到 file 文件中
cmd >> file
stdout 重定向到 file 文件中(追加)
cmd 1> fiel
stdout 重定向到 file 文件中
cmd > file 2>&1
stdout stderr 一起重定向到 file 文件中
cmd 2> file
stderr 重定向到 file 文件中
cmd 2>> file
stderr 重定向到 file 文件中(追加)
cmd >> file 2>&1
stderr stderr 一起重定向到 file 文件中
cmd < file >file2 cmd
命令以 file 文件作为 stdin,以 file2
件作为 stdout
cat <>file
以读写的方式打开 file
cmd < file cmd
命令以 file 文件作为 stdin
cmd << delimiter Here document
,从 stdin 中读入,直至遇到
delimiter
分界符


3
、进阶IO
>&n
使用系统调用 dup (2) 复制文件描述符 n 并把结果用作标准输出
<&n
标准输入复制自文件描述符 n
<&-
关闭标准输入(键盘)
>&-
关闭标准输出
n<&-
表示将 n 号输入关闭
n>&-
表示将 n 号输出关闭
上述所有形式都可以前导一个数字,此时建立的文件描述符由这个数字指定而不是缺省
0 1。如:
... 2>file
运行一个命令并把错误输出(文件描述符 2)定向到 file
... 2>&1
运行一个命令并把它的标准输出和输出合并。(严格的说是
通过复制文件描述符 1 来建立文件描述符 2 ,但效果通常是合并了两个流。)

2>&1
说明:2>&1 也就是 FD2FD1 ,这里并不是说FD2 的值 等于FD1的值,因为 >
是改变送出的数据信道,通俗的说是:把stderr并到stdout
但使用类似 cmd 1>&3 这样的形式时,原理相同,但往往不同于 2>&1 1>&2 通常
用来合并的作用。

注意:普通cmd命令的cmd n>&n exec n>&n 是有区别的。

exec 0<infilename #
打开文件infilename作为 stdin
exec 1>outfilename #
打开文件outfilename作为stdout
exec 2>errfilename #
打开文件 errfilename作为 stderr
exec 0<&- #
关闭 FD0
exec 1>&- #
关闭 FD1
exec 5>&- #
关闭 FD5

问:
如果关闭了 FD0FD1FD2,其后果是什么?
恢复 FD0FD1FD2 关闭FD0FD1FD2 有什么区别?代码分别是什么?
打开了FD3FD9,我们用完之后,你觉得是将他们关闭还是恢复?


下面是提示(例子来源于CU):
exec 6>&2 2>ver # FD2(
本来往monitor送的) 定向到文件ver
command >>dev/null & #
丢弃FD1(stdout)
exec 2>&6 #
恢复 FD2


4
、简单举例(其中 yes.txt存在,no.txt不存在)
a
stdout stderr 都通过管道送给egrep了:
(ls yes.txt 2>&1;ls no.txt 2>&1) 2>&1|egrep \* >file
(ls yes.txt;ls no.txt) 2>&1|egrep \* >file

###
这个例子就是让大家:理解 命令执行顺序 管道“|”
在命令执行前,先要进行重定向的处理,并将把 nested sub-shell stdout 接到
egrep
命令的 stdin
nested sub-shell
,在 ( ) 中的两个命令可以看作一个命令。其 stdout(FD1)
“|” 作为 egrep stdin,再加上 2>&1 时,初始 stdout stderr 都往管道
“|”
送。
###

b
、没有任何东西通过管道送给egrep,全部送往monitor
(ls yes.txt 2>&1;ls no.txt 2>&1) >&2|egrep \* >file
虽然在()里面将 FD2转往FD1,但在()外,遇到 >&2 ,结果所有的都送到monitor


5
、中阶例子(其中 you 这个文件是存在的,no wu 这两个文件不存在)
r2007
兄的:http://bbs./forum/view...1848&show_type=
new&sid=cf30398c911e0d2b16313c6922123f67

条件:stderr通过管道送给egrep,正确消息仍然送给monitor(不变)

exec 4>&1;(ls you no 2>&1 1>&4 4>&-;ls wu 2>&1 1>&4 4>&-)|egrep \* >file;
exec 4>&-
或者
exec 4>&1;(ls you no;ls wu) 2>&1 1>&4 4>&-|egrep \* >file;exec 4>&-

r2007
兄在其贴已有详细说明,如果加两个条件:
1)要求cmd1cmd2并行运行;
2)将cmd1的返回值赋给变量 ss

则为:
exec 3>&1;exec 4>&1
ss=$(((ls you no 2>&1 1>&3 3>&-;echo $? >&4)|egrep \* >file) 4>&1)
exec 3>&-;exec 4>&-

说明:
exec 3>&1;4>&1
###
建立FD3,是用来将下面ls那条语句(子shell)中的FD1 恢复到正常FD1,即输出
monitor,你可以把FD3看作最初始的FD1的硬盘备份(即输出到monitor);
###
建立FD4,到时用作保存ls的返回值(echo $?),你可以将FD4看作你考试时用于
存放计算“echo $?”的草稿纸;

(ls you no 2>&1 1>&3 3>&-;echo $? >&4)
###
大家还记得前面说的子shell和管道吧。这条命令首先会继承FD0FD1FD2FD3FD4,它位于管道前,所
以在运行命令前会先把子shell自己的FD1和管道“|”相连。
但是我们的条件是stderr通过管道送往egrepstdout仍然输出到monitor
于是通过2>&1,先把 shellFD1 的管道送给”FD2,于是子shell中的stderr送往
管道“|”
再通过 1>&3,把以前的硬盘备份恢复给子shellFD1,于是子shell中的FD1变成
送到monitor了。
再通过3>&- ,将3关闭;
接着运行echo $? ,本来其输出值应该送往管道的,通过 >&4 ,将 输出 送往草稿
”FD4,留以备用。

((ls you no 2>&1 1>&3 3>&-;echo $? >&4)|egrep \* >file)
于是,stderr 通过管道送给 egrep stdout 送给monitor,但是,还有 FD4,它送到
哪去了?
$(((ls you no 2>&1 1>&3 3>&-;echo $? >&4)|egrep \* >file) 4>&1)
最后的 4>&1 ,就是把FD4 重定向到 FD1。但由于其输出在 $( )中,其值就赋给变量
ss
了。

最后一行关闭 FD3FD4


6
、高阶例子
lightspeed
版主大大的:Shell 经典问题之 [ I/O 重定向]
http://bbs./forum/view...show_type=new
[Q]
对于命令 cmd1, cmd2, cmd3, cmd4. 如何利用单向管道完成下列功能:
1.
所有命令并行执行
2. cmd1
cmd2 不需要 stdin
3. cmd1
cmd2 stdout 定向到 cmd3 stdin
4. cmd1
cmd2 stderr 定向到 cmd4 stdin
5. cmd3
stdout 定向到文件 a, stderr 定向到屏幕
6. cmd4
stdout 定向到文件 b, stderr 定向到屏幕
7. cmd1
的返回码赋给变量 s
8.
不能利用临时文件

解决方法:
exec 3>&1; exec 4>&1
my_value=$(((((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>&1 | cmd3 >a 2>&3 )
2>&1 | cmd4 >b ) 4>&1)
exec 3>&-; exec 4>&-

解释:
exec 3>&1; exec 4>&1
建立FD3 ,给cmd1恢复其FD1用和给cmd3 恢复其FD2用;
建立FD4,保存“echo $?”输出值的草稿纸

第一对括号到第一个管道:(cmd1 1>&3 ; echo $? >&4 )|
cmd1
本身没有stdin,其stdout原要送往第一个管道,由于1>&3的作用,其stdout
送往FD3;而 >&4 的作用实际是将 cmd1 运行后的返回码送往 FD4cmd1stderr默认
等待下一步处理。最后,没有往管道送任何东西;

第二对括号到第二个管道:((cmd1 1>&3 ; echo $? >&4 )| cmd2) 3>&1|
由于第一个括号中cmd1 stdout 被送往 FD3,导致管道左端没有任何输入,cmd2
从而就没有stdincmd2 stdout则为默认的;
将第二对括号看出一个命令,其所有的stdout送往第二个管道“|”;同时由于3>&1
的作用,原先cmd1stdout在送往FD3 又与cmd2stdout并到一起,所以cmd1 cmd2
stdout 都送往第二个管道“|”。而cmd1cmd2stderr仍然默认等待下一步处理;

第三对括号到第三个管道:(((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>&1 |
cmd3 >a 2>&3 ) 2>&1|
cmd3 >a 2>&3
cmd3接收处理来自管道的stdin后,其 stdout 送给文件a,其stderr
送往FD3,由于FD3继承FD1,实际上其stderr是送往monitor。如果没有“2>&3”,那么
cmd3
stderr就会干扰cmd1cmd2的错误输出,所以它是必须的;
将第三个括号里完全看作一个命令,其stdout送往管道 “|”,由于2>&1,于是
stderr
也送往着管道。但由于cmd1cmd2stdout已经送给了cmd3处理,而cmd3
stdout
输出到文件acmd3stderr也送往monitor,所以实际上只有cmd1cmd2
stderr
送往管道。

cmd4 >b
cmd4接收处理来自管道的stdin后,其stdout 定向到文件 b
stderr
默认输出到monitor

第四对括号:( (((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>&1 | cmd3 >a
2>&3 ) 2>&1| cmd4 >b ) 4>&1
四对括号里面所有命令的 FD1FD2都处理完了,但是还有“echo $? >&4”没处理。
“4>&1”
的作用就是FD4并到stdout”,但由于其他命令的stdout都处理完了,实际
上就只有 $? 的值。
又由于 $() 会建立一个管道,输入端为()内命令,故 $? 的值被赋给变量my_value

最后一行是关闭FD3FD4
另:恢复重定向或关闭的stdoutexec 1>&2 ,恢复重定向或关闭的stderrexec
2>&1
。如果stdoutstderr全部都关闭了,又没有保存原来的FD,可以用:exec 1>
/dev/tty
恢复。

+++++++++++++++++++++++++++++++
我尝试回答下面的问题。如有错误,还请各位前辈指正!


7
、在一个交互式的(Interactive) shell , exec 进行 I/O 重定向.
1
. Stdin, stderr 可以定向到文件中吗? 有什么结果?
a
、在交互式shell中,可以将stdin定向到文件。执行:exec 0<in
结果为:in 文件中每一行均会被自动执行,并且在最后会再加执行一个 exit 命令,
导致退出(或退回到正常shell下)。
in 文件内容:$ more in
date
read lsp
echo hahha
echo "this is $lsp"

在提示符下执行命令:$ exec 0<in (以下为自动输出,除 # # 后那行的内容)
$ date
Tue Jan 18 18:29:07 HKT 2005
$ read lsp #
其下面本应有的那句“ echo hahha ” “hahaha”
已经被读入到变量 lsp 中了
$ echo "this is $lsp"
this is echo hahha
$ exit

b
、在交互式shell中,可以将stderr定向到文件。执行:exec 2>err
结果为:命令提示符PS被屏蔽,输入的命令也被屏蔽。但是命令执行的结果,如果是
stdout
则会回显到屏幕上,如果是 stderr 则不会回显到屏幕上。其中,命令提示符、
命令、stderr均会保存到文件 err 中。如:
$ exec 2>err
err in out #
执行 ls 命令
Tue Jan 18 18:55:58 HKT 2005 #
执行 date 命令,而后执行了“ ls
nofile”
nofile这个文件不存在
$ #
执行 exit 命令

现在让我们查看 err文件:
$ more err
[lsp@ii lsp]$ ls
[lsp@ii lsp]$ date
[lsp@ii lsp]$ ls nofile
ls: nofile: No such file or directory
[lsp@ii lsp]$ exit
exit

c
、在交互式shell中,可以将stdout定向到文件。这个使我们常用到的。就不说了。
就是将错误的输出内容定向到文件中。正确的输出内容并不受影响。

2
. Stdin, Stderr 可以关闭吗? 有什么结果?
在交互式shell中,如果关闭stdin,如:exec 0<&- ,其结果是退出(或退回到正常
shell
下)。
在交互式shell中,如果关闭stderr,如:exec 2>&- ,状态同stderr定向到文件,唯一
不同的是没有保存下来。
在交互式shell中,如果关闭stdoutr,如:exec 1>&- ,只要执行有stdoutstderr内容
送往 monitor 的命令,如lsdate这类命令,均会报错:“ls: write error: Bad file
descriptor”
。其他如cdmkdir……这类命令不受影响。

3
. 如果 stdin, stdout, stderr 进行了重定向或关闭, 但没有保存原来的 FD,
以将其恢复到 default 状态吗?
***
如果关闭了stdin,因为会导致退出,那肯定不能恢复。
***
如果重定向或关闭 stdoutstderr其中之一,可以恢复,因为他们默认均是送往
monitor
(但不知会否有其他影响)。如恢复重定向或关闭的stdoutexec 1>&2 ,恢复
重定向或关闭的stderrexec 2>&1
***
如果stdoutstderr全部都关闭了,又没有保存原来的FD,可以用:exec 1>
/dev/tty
恢复。



8
cmd >a 2>a cmd >a 2>&1 为什么不同?
cmd >a 2>a
stdoutstderr都直接送往文件 a a文件会被打开两遍,由此导致
stdout
stderr互相覆盖。
cmd >a 2>&1
stdout直接送往文件a stderr是继承了FD1的管道之后,再被送往
文件a a文件只被打开一遍,就是FD1将其打开。

他们的不同点在于:
cmd >a 2>a
相当于使用了FD1FD2两个互相竞争使用文件 a 的管道;
cmd >a 2>&1 只使用了一个管道FD1,但已经包括了stdoutstderr
IO效率上来讲,cmd >a 2>&1的效率更高。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多