> 與 < 差在哪?
谈到I/O redirection,不妨先让我们认识一下File Descriptor(FD)。程序的运算,在大部份情况下都是进行数据(data)的处理,这些数据从哪读进?又,送出到哪里呢?这就是File descriptor(FD)的功用了。
在shell程序中,最常使用的FD大概有三个,分别为:
- 0: Standard Input(STDIN)
- 1: Standard Output(STDOUT)
- 2: Standard Error Output(STDERR)
在标准情况下,这些FD分别跟如下设备(device)关联:
- stdin(0):keyboard
- stdout(1):monitor
- stderr(2):monitor
我们可以用如下下命令测试一下:
$ mail -s test root this is a test mail. please skip.</div>
^d(同时按crtl跟d键)
很明显,mail程序所读进的数据,就是从stdin也就是keyboard读进的。不过,不见得每个程序的stdin都跟mail一样从keyboard读进,因为程序作者可以从档案参数读进stdin,如:
$ cat /etc/passwd</div>
但,要是cat之后没有档案参数则又如何呢?哦,请您自己玩玩看啰….^_^
$ cat</div>
(请留意数据输出到哪里去了,最后别忘了按^d离开…)至于stdout与stderr,嗯…然后,让我们继续看stderr好了。
事实上,stderr没甚么难理解的:说穿了就是“错误信息”要往哪边送而已…比方说,若读进的档案参数是不存在的,那我们在monitor上就看到了:
$ ls no.such.file ls: no.such.file: No such file or directory</div>
若,一个命令同时产生stdout与stderr呢?那还不简单,都送到monitor来就好了:
$ touch my.file $ ls my.file no.such.file ls: no.such.file: No such file or directory my.file</div>
okay,至此,关于FD及其名称、还有相关联的设备,相信你已经没问题了吧?那好,接下来让我们看看如何改变这些FD的预设数据信道,我们可用<来改变读进的数据信道(stdin),使之从指定的档案读进。我们可用>来改变送出的数据信道(stdout,stderr),使之输出到指定的档案。比方说:
$ cat < my.file</div>
就是从my.file读进数据
$ mail -s test root < /etc/passwd</div>
则是从/etc/passwd读进…
这样一来,stdin将不再是从keyboard读进,而是从档案读进了…严格来说,<符号之前需要指定一个FD的(之间不能有空白),但因为0是<的预设值,因此<与0<是一样的!okay,这个好理解吧?
那,要是用两个<<又是啥呢?这是所谓的HERE Document,它可以让我们输入一段文本,直到读到<<后指定的字串。比方说:
$ cat <<FINISH first line here second line there third line nowhere FINISH</div>
这样的话,cat会读进3行句子,而无需从keyboard读进数据且要等^d结束输入。
okay,又到讲古时间~~~当你搞懂了0<原来就是改变stdin的数据输入信道之后,相信要理解如下两个redirection就不难了:1> 2> 。前者是改变stdout的数据输出信道,后者是改变stderr的数据输出信道。两者都是将原本要送出到monitor的数据转向输出到指定档案去。
由于1是>的预设值,因此,1>与>是相同的,都是改变stdout。用上次的ls例子来说明一下好了:
$ ls my.file no.such.file 1>file.out ls: no.such.file: No such file or directory</div>
这样monitor就只剩下stderr而已。因为stdout给写进file.out去了。
$ ls my.file no.such.file 2>file.err my.file</div>
这样monitor就只剩下stdout,因为stderr写进了file.err。
$ ls my.file no.such.file 1>file.out 2>file.err</div>
这样monitor就啥也没有,因为stdout与stderr都给转到档案去了…
呵~~~看来要理解>一点也不难啦﹗是不?没骗你吧?^_^ 不过,有些地方还是要注意一下的。
首先,是同时写入的问题。比方如下这个例子:
$ ls my.file no.such.file 1>file.both 2>file.both</div>
假如stdout(1)与stderr(2)都同时在写入file.both的话,则是采取“覆盖”方式:后来写入的覆盖前面的。让我们假设一个stdout与stderr同时写入file.out的情形好了:
- 首先stdout写入10个字元
- 然后stderr写入6个字元
那么,这时候原本stdout输出的10个字元就被stderr覆盖掉了。那,如何解决呢?所谓山不转路转、路不转人转嘛,我们可以换一个思维:将stderr导进stdout或将stdout导进sterr,而不是大家在抢同一份档案,不就行了﹗bingo﹗就是这样啦:
- 2>&1就是将stderr并进stdout作输出
- 1>&2或>&2就是将stdout并进stderr作输出
于是,前面的错误操作可以改为:
$ ls my.file no.such.file 1>file.both 2>&1</div>
或
$ ls my.file no.such.file 2>file.both >&2</div>
在Linux档案系统里,有个设备档位于/dev/null。许多人都问过我那是甚么玩意儿?我跟你说好了:那就是“空”啦﹗没错﹗空空如也的空就是null了….请问施主是否忽然有所顿误了呢?然则恭喜了~~~ ^_^ 这个null在I/O Redirection中可有用得很呢:
- 若将FD1跟FD2转到/dev/null去,就可将stdout与stderr弄不见掉。
- 若将FD0接到/dev/null来,那就是读进nothing。
比方说,当我们在执行一个程序时,画面会同时送出stdout跟stderr,
假如你不想看到stderr(也不想存到档案去),那可以:
$ ls my.file no.such.file 2>/dev/null my.file</div>
若要相反:只想看到stderr呢?还不简单﹗将stdout弄到null就行:
$ ls my.file no.such.file >/dev/null ls: no.such.file: No such file or directory</div>
那接下来,假如单纯只跑程序,不想看到任何输出结果呢?哦,这里留了一手上次节目没讲的法子,专门赠予有缘人﹗…^_^ 除了用>/dev/null 2>&1之外,你还可以如此:
$ ls my.file no.such.file &>/dev/null</div>
(提示:将&>换成>&也行啦~~!)
okay?讲完佛,接下来,再让我们看看如下情况:
$ echo "1" > file.out $ cat file.out 1 $ echo "2" > file.out $ cat file.out 2</div>
看来,我们在重导stdout或stderr进一份档案时,似乎永远只获得最后一次导入的结果。那,之前的内容呢?呵~~~要解决这个问提很简单啦,将>换成>>就好:
$ echo "3" >> file.out $ cat file.out 2 3</div>
如此一来,被重导的目标档案之内容并不会失去,而新的内容则一直增加在最后面去。easy?呵…^_^
但,只要你再一次用回单一的>来重导的话,那么,旧的内容还是会被“洗”掉的﹗这时,你要如何避免呢?----备份﹗yes,我听到了﹗不过….还有更好的吗?既然与施主这么有缘份,老纳就送你一个锦囊妙法吧:
$ set -o noclobber $ echo "4" > file.out -bash: file: cannot overwrite existing file</div>
那,要如何取消这个“限制”呢?哦,将set -o换成set +o就行:
$ set +o noclobber $ echo "5" > file.out $ cat file.out 5</div>
再问:那…有办法不取消而又“临时”盖写目标档案吗?哦,佛曰:不可告也﹗啊~开玩笑的、开玩笑的啦~ ^_^唉,早就料到人心是不足的了﹗
$ set -o noclobber $ echo "6" >| file.out $ cat file.out 6</div>
留意到没有:在>后面再加个“|”就好(注意:>与|之间不能有空白哦)… 呼…(深呼吸吐纳一下吧)~~~ ^_^再来还有一个难题要你去参透的呢:
$ echo "some text here" > file $ cat < file some text here $ cat <

