linux重定向命令应用及语法

I/O重定向详解及应用实例

1、基本概念(这是理解后面的知识的前提,请务必理解)

a、I/O重定向通常与FD有关,shell的FD通常为10个,即0~9;

b、常用FD有3个,为0(stdin,标准输入)、1(stdout,标准输出)、2(stderr,标准错误输出),默认与keyboard、monitor、monitor有关;

c、用<来改变读进的数据信道(stdin),使之从指定的档案读进;

d、用>来改变送出的数据信道(stdout,stderr),使之输出到指定的档案;

e、0是<的默认值,因此<与0<是一样的;同理,>与1>是一样的;

f、在IO重定向中,stdout与stderr的管道会先准备好,才会从stdin读进资料;

g、管道“|”(pipeline):上一个命令的stdout接到下一个命令的stdin;

h、tee命令是在不影响原本I/O的情况下,将stdout复制一份到档案去;

i、bash(ksh)执行命令的过程:分析命令-变量求值-命令替代(``和$())-重定向-通配符展开-确定路径-执行命令;

j、()将commandgroup置于sub-shell去执行,也称nestedsub-shell,它有一点非常重要的特性是:继承父shell的Standardinput,output,anderrorplusanyotheropenfiledescriptors。

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

2、基本IO

cmd>file把stdout重定向到file文件中;

cmd>>file把stdout重定向到file文件中(追加);

cmd1>fiel把stdout重定向到file文件中;

cmd>file2>&1把stdout和stderr一起重定向到file文件中;

cmd2>file把stderr重定向到file文件中;

cmd2>>file把stderr重定向到file文件中(追加);

cmd>>file2>&1把stderr和stderr一起重定向到file文件中(追加);

cmd<file>file2cmd命令以file文件作为stdin,以file2文件作为stdout;

cat<>file以读写的方式打开file;

cmd<filecmd命令以file文件作为stdin;

cmd<<delimiterHeredocument,从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也就是FD2=FD1,这里并不是说FD2的值等于FD1的值,因为>是改变送出的数据信道,也就是说把FD2的“数据输出通道”改为FD1的“数据输出通道”。如果仅仅这样,这个改变好像没有什么作用,因为FD2的默认输出和FD1的默认输出本来都是monitor,一样的!但是,当FD1是其他文件,甚至是其他FD时,这个就具有特殊的用途了。请大家务必理解这一点。

exec0exec1>outfilename#打开文件outfilename作为stdout。

exec2>errfilename#打开文件errfilename作为stderr。

exec0<&-#关闭FD0。

exec1>&-#关闭FD1。

exec5>&-#关闭FD5。

问:如果关闭了FD0、FD1、FD2,其后果是什么?恢复FD0、FD1、FD2与关闭FD0、FD1、FD2有什么区别?代码分别是什么?打开了FD3~FD9,我们用完之后,你觉得是将他们关闭还是恢复?

下面是提示(例子来源于CU一帖子,忘记出处,来日再补上):

exec6>&22>ver

command>>dev/null&

exec2>&6#恢复FD2

4、简单举例

a、stdout和stderr都通过管道送给egrep了:

(lsyouno2>&1;lsyes2>&1)2>&1|egrep\*>file

(lsyouno2>&1;lsyes2>&1)|egrep\*>file

(lsyouno;lsyes)2>&1|egrep\*>file

这个例子要注意的就是:

理解命令执行顺序和管道“|”:在命令执行前,先要进行重定向的处理,并将把nestedsub-shell的stdout接到egrep命令的stdin。nestedsub-shell,在()中的两个命令加上(),可以看作一个命令。其FD1已经连接到“|”往egrep送了,当遇到2>&1时,也就是FD2=FD1,即FD2同FD1一样,往管道“|”那边送。

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

(lsyouno2>&1)1>&2|egrep\*>file##送到monitor

lsyouno2>&11>&2|egrep\*>file##送给管道“|”

lsyouno1>&22>&1|egrep\*>file##送到monitor

5、中阶例子

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

exec4>&1;(lsyouno2>&11>&44>&-;lsyes2>&11>

&44>&-)|egrep\*>file;exec4>&-

或者

exec4>&1;(lsyouno;lsyes)2>&11>

&44>&-|egrep\*>file;exec4>&-

如果加两个条件:

(1)要求cmd1和cmd2并行运行;

(2)将cmd1的返回值赋给变量ss。

则为:

exec3>&1;exec4>&1

ss=$(((lsyouno2>&11>&33>&-;echo$?>&4)|egrep\*>file)4>&1)

exec3>&-;exec4>&-

说明:

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

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

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

6、高阶例子

命令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.不能利用临时文件。

解决方法:

exec3>&1;exec4>&1

s=$(((((cmd11>&3;echo$?>&4)|cmd2)3>

&1|cmd3>a2>&3)2>&1|cmd4>b)4>&1)

exec3>&-;exec4>&-

这个我一步步解释(好复杂,自己感觉看明白了,过一会再看,大脑仍然有几分钟空白~~~,没想到我也能看明白。exec3>&1;exec4>&1前面的例子都有说明了,就是建立FD3,给cmd1恢复其FD1用和给cmd3恢复其FD2用,建立FD4,保存“echo$?”输出值的“草稿纸”。

第一对括号:(cmd11>&3;echo$?>&4)和其后(第一个)管道。在第一个括号(子shell)中,其FD1已经连到管道中了,所以用FD3将FD1恢复正常,不让他往管道跑;这里的cmd1没有stdin,接着将cmd1运行的返回码保存到FD4中。

第二对括号:((cmd11>&3;echo$?>&4)|cmd2)3>&1和其后(第二个)管道。前面的FD1已经不送给cmd2了,FD2默认也不送过来,所以cmd2也没有stdin,所以在第二对括号里面:cmd1和cmd2的stdout、stderr为默认输出,一直遇到“3>&1”为止。请注意:“3>&1”,先将第二对括号看出一个命令,他们遇到第二个管道时,其FD1连到管道“|”,由于“3>&1”的作用,子shell的FD1送给FD3使用,所以所有FD3的输出都“流往”cmd3,又由于继承关系(继承第一行的命令),FD3实际上就是cmd1和cmd2的stdout,于是“cmd1和cmd2的stdout定向到cmd3的stdin”

第三对括号:(((cmd11>&3;echo$?>&4)|cmd2)3>&1|cmd3>a2>&3)2>&1和其后的第三个管道。cmd1和cmd2的stdout已经定向到cmd3的stdin,处理之后,cmd3>a意味着将其stdout送给a文件。而2>&3的意思是:恢复cmd3的错误输出为FD3,即送往monitor。于是“cmd3的stdout定向到文件a,stderr定向到屏幕”。如果没有“2>&3”,那么cmd3的错误输出就会干扰cmd1和cmd2的错误输出,所以它是必须的!请注意第三对括号后的“2>&1”|,其子shell的FD1本来连接着管道“|”,但子shellFD1慷慨大方,送给了FD2,于是FD2连接着管道。还记得前面的cmd1和cmd2吗?他们的stderr一直没动了。于是在这里,通过管道送给了第四个命令cmd4了。即“cmd1和cmd2的stderr定向到cmd4的stdin”。后面就比较简单了。cmd4>b表示“cmd4的stdout定向到文件b,stderr定向到屏幕(默认)”

第四对括号:((((cmd11>&3;echo$?>&4)|cmd2)3>&1|cmd3>a2>&3)2>&1|cmd4>b)与其后的4>&1。四对括号里面的FD1、FD2都处理完了。但是还记得前面“echo$?>&4”那块“草稿纸”吗?“4>&1”的作用就是“将草稿纸上的内容送给monitor”,但是由于最外面还有$()将其“包着”。于是其值赋给变量“s”。

相关推荐