鸟哥的私房菜:Bash shell(五)-数据流重导向
数据流重定向
数据流重导向就是将某个指令执行后应该要出现在屏幕上的数据, 给他传输到其它的地方,例如档案或者是装置 (例如打印机之类的!)!这玩意儿在 Linux 的文字模式底下可重要的! 尤其是如果我们想要将某些数据储存下来时,就更有用了!
一般来说,如果你要执行一个指令,通常他会是这样的:
我们执行一个指令的时候,这个指令可能会由档案读入资料,经过处理之后,再将数据输出到屏幕上。 在图中, standard output 与 standard error 分别代表标准输出与标准错误输出, 这两个玩意儿预设都是输出到屏幕上面来的啊!举个简单例子来说, 我们下达『 cat /etc/crontab /etc/vbirdsay 』这个指令时,cat 会由 /etc/crontab 与 /etc/vbirdsay 读入数据, 然后再将数据输出到屏幕上,不过,因为系统本来就不存在
/etc/vbirdsay 这个档案, 所以就会显示错误讯息,这个错误讯息也会输出到屏幕上来喔! 在这样的过程当中,我们可以将 standard error (简称 stderr) 与 standard output (简称 stdout) 给他传送到其它不同的地方,而不是屏幕上头!传送的目标处,通常是档案或者是装置! 而传送的指令则是如下所示:
1. 标准输入(stdin) :代码为 0 ,使用 < 或 << ;
2. 标准输出(stdout):代码为 1 ,使用 > 或 >> ;
3. 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;
举例来说,如果我想要将我目前根目录下所有的目录都记录下来的话,也就是说,将 ls -l / 这个指令的输出结果储存下来,就可以:
[root@linux ~]# ls -l / > ~/rootfile
# 本来 ls -l / 会将根目录的数据列出到屏幕上;
# 现在我使用了 > ~/rootfile 后,则本来应该在屏幕上出现的数据
# 就会被『重新导向』到 ~/rootfile 档案内了!就可以将该数据储存!
此时,原本应该在屏幕上面出现的数据通通不见去~因为那些资料都被写入到 ~/rootfile 去了! 当然,那个档案的档名随便你取啦~如果你下达:『 cat ~/rootfile 』就可以看到原本应该在屏幕上面的数据啰。 那么如果我再次下达:『 ls -l /home > ~/rootfile 』后,那么那个 ~/rootfile 档案的内容变成什么? 呵呵!变成『仅有 ls -l /home 的数据』而已!咦!原本的 ls -l /
数据就不见了吗?是的! 因为该档案的建立方式是:
1. 该档案 (本例中是 ~/rootfile) 若不存在,系统会自动的将他建立起来,但是,
2. 当这个档案存在的时候,那么系统就会先将这个档案内容清空,然后再将数据写入!
3. 也就是若以 > 输出到一个既存盘案中,呵呵,那个档案就会被覆盖掉啰!
那如果我想要将数据累加,不想要将旧的数据删除,那该如何是好? 呵呵!就利用 >> 就好啦!例如上面的例子中,就变成『ls -l / >> ~/rootfile』 如此一来,当 ~/rootfile 不存在时,系统会主动建立这个档案,若该档案已存在, 则数据会在该档案的最下方累加进去!基本上,指令的下达方式:
command > 1> 2> 2>> < 装置或档案
当然啦,一串指令的最左边一定是指令,而在 >,2>,< 右边的,必须是档案或装置才行! 此外,那个 > 会等于 1> ,因为 standard output 代码是 1 ,可以省略啦! 再者, 1 与 > 之间并没有空格喔!是紧接在一起的!注意注意!我们底下来玩几个东西好了:
范例一:将目前目录下的档案信息全部储存到 list.txt 档案中
[root@linux ~]# ls -al > list.txt
范例二:将根目录下的数据也储存到 list.txt 档案中
[root@linux ~]# ls -al / >> list.txt
好了,对于『 > , >> 』这两个东西有一定的概念之后,我们来深入的谈一谈『数据流重导向』的观念吧! 如前所述,基本上, Linux 执行的结果中,可以约略的分成『正确输出』与『错误输出』两种数据。 例如,当你以一般身份执行 find 这个指令时,例如执行『 find / -name testing 』时,由于你是一般身份,又有些数据夹是不允许一般身份者进入的,
所以啰,当你使用 find 时,就会有错误讯息发生了!但同时如果有 testing 这个档案在你可以进入的资料夹当中,那么屏幕也会输出到给你看!因此, 就具有正确的与错误的输出两种啰!(分别称为 Stdout 与 Stderror)例如下面为执行结果: 里面的『 find: /home/root: Permission denied 』就告诉你该数据夹你没有权限进入, 这就是错误的输出了,那么『 /home/dmtsai/tseting 』就是正确的输出了!
[dmtsai@linux ~]$ find /home -name testing
find: /home/test1: Permission denied <== Starndard error
find: /home/root: Permission denied <== Starndard error
find: /home/masda: Permission denied <== Starndard error
/home/dmtsai/testing <== Starndard output
好了,那么假如我们想要将数据输出到 list 这个档案中呢?执行『 find / -name testing > list 』 会有什么结果?呵呵,你会发现 list 里面存了刚刚那个『正确』的输出数据, 至于屏幕上还是会有错误的讯息出现呢!伤脑筋!如果想要将正确的与错误的数据分别存入不同的档案中需要怎么做?! 呵呵!其实在数据的重导向方面,正确的写法应该是『 1> 』与『 2> 』才对!但是如果只有 > 则预设是以 1> 来进行数据的!那个
1> 是输出正确数据, 2> 则是错误数据输出项目。也就说:
1> :是将正确的数据输出到指定的地方去
2> :是将错误的数据输出到指定的地方去
好了,那么上面的例子中,我们如何将数据输出到不同的地方去呢?可以这么写:
[dmtsai@linux ~]$ find /home -name testing > list_right 2> list_error
这样一来,刚刚执行的结果中,有 Permission 的那几行错误信息都会跑到 list_error 这个档案中,至于正确的输出数据则会存到 list_right 这个档案中啰!这样可以了解了吗? 如果有点混乱的话,去休息一下再来看看吧!! 再来,如果我只要正确的数据,错误的信息我不要了呢?呵呵,这个时候 /dev/null 这个垃圾桶就很重要了!/dev/null
是什么呢? 基本上,那就有点像是一个『黑洞』的垃圾桶功能!当你输入的任何东西导向到这个虚拟的垃圾桶装置时, 『他就会凭空消失不见了~~』,这个东西有用的很!例如上面的例子中,我们可以这么做,来将错误的信息丢掉!
[dmtsai@linux ~]$ find /home -name testing > list_right 2> /dev/null
很神奇呦! error message 就会『不见了!』呵呵!真高兴!另外, 如果我要将数据都写到同一个档案中呢?这个时候写法需要用到特殊写法,请注意底下的写法呦!
[dmtsai@linux ~]$ find /home -name testing > list 2> list <==错误写法
[dmtsai@linux ~]$ find /home -name testing > list 2>&1 <==正确写法
请特别留意这一点呢!同时写入同一个档案需要使用 2>&1 才对呦! OK!了解了 >, 2>, >> 与 /dev/null 之后,那么那个 < 又是什么呀!?呵呵!以最简单的说法来说, 那就是『将原本需要由键盘输入的数据,经由档案来读入』的意思。 举例来说,我们可以使用 cat 在键盘上面输入一些数据,然后写入一个档案内,例如:
[root@linux ~]# cat > catfile
testing
cat file test
<==这里按下 [ctrl]+d 结束输入来离开!
此时就会有 catfile 这个档案产生,而且该档案的内容就是刚刚输入的内容喔。 那么,我是否可以使用其它档案来取代键盘输入呢?可以啊!这样做!
[root@linux ~]# cat > catfile < somefile
我可以先编辑 somefile ,然后再以上述的指令来将数据输出到 catfile 去呢!这样可以理解了吗? 能够理解 < 之后,再来则是怪可怕一把的 << 这个连续两个小于的符号了~ 他代表的是『结束的输入字符』的意思!举例来讲:『我要用 cat 直接将输入的讯息输出到 catfile 中, 且当输入 eof 时,该次输入就结束』,那我可以这样做:
[root@linux ~]# cat > catfile <<eof
> This is a test testing
> OK now stop
> eof <==输入这个玩意儿,嘿!立刻就结束了!
看到了吗?利用 << 右侧的控制字符,我们可以终止一次输入, 而不必输入 [crtl]+d 来结束哩!这对程序写作很有帮助喔!好了,那么为何要使用命令输出重导向呢? 这个问题一定会困扰你一下下的,如果你从来都没有写过 script 的话!好了,我们来说一说吧!
- 当屏幕输出的信息很重要,而且我们需要将他存下来的时候
- 背景执行中的程序,不希望他干扰屏幕正常的输出结果时;
- 一些系统的例行命令(例如写在 /etc/crontab 中的档案)的执行结果,希望他可以存下来时;
- 一些执行命令,我们已经知道他可能的错误讯息,所以想以『 2> /dev/null 』将他丢掉时;
- 错误讯息与正确讯息需要分别输出时。
当然还有很多很多的功能的,最简单的就是网友们常常问到的:『 为何我的 root 都会收到系统 crontab 寄来的错误讯息呢』这个咚咚是常见的错误, 而
如果我们已经知道这个错误讯息是可以忽略的时候,嗯!『 2> errorfile 』这个功能就很重要了吧! 了解了吗??
命令执行的判断依据: ; , &&, ||
在某些时候,我们希望可以一次执行多个指令,例如关机时,希望我可以先执行两次 sync ,然后才 shutdown 计算机,那么可以怎么作呢?这样做呀:[root@linux ~]# sync; sync; shutdown -h now
在指令与指令中间利用分号 (;) 来隔开,这样一来,分号前的指令执行完后, 就会立刻接着执行后面的指令了。这真是方便啊~再来,换个角度来想, 万一我想要在某个目录底下建立一个档案,也就是说,如果该目录存在的话, 那我才建立这个档案,如果不存在,那就算了~目录是否存在可以使用一些 bash 提供的判断式功能, 但这里假设我不晓得那个指令,但我知道我可以使用 ls 来判断是否有该目录的存在, 也就是说,我可以利用 ls directoryname 判断是否存在,然后以 touch 建立一个档案, 这两个指令有相关性,那该如何写呢?呵呵!可以利用 && 来作喔!
[root@linux ~]# ls /tmp && touch /tmp/testingagin
是否记得我们在变量的章节里面谈过这个奇怪的变数『 $? 』呢? 如果指令执行结果没有错误讯息,那就会回传 $?=0 ,如果有错误, 那回传值就不会是 0 啊!经由这样的判断,我们也可以利用 && 来决定, 当前面的指令执行结果为正确 (例如:仅有 standard output 时),就可以接着执行后续的指令, 否则就予以略过!因此,当 ls /tmp 没有问题,那么就会接着执行 touch /tmp/testingagin 了! 万一是这样:
[root@linux ~]# ls /vbird && touch /vbird/test
因为我的系统里面根本就不可能存在 /vbird 这个目录呢!所以,执行 ls /vbird 就会回传错误, 那么后续的 touch /vbird/test 自然就不会动作啰!了解吗? 再换个角度来想,如果我想要当某个档案不存在时,就去建立那个档案, 否则就略过呢?很简单啊~可以这样做:
[root@linux ~]# ls /tmp/vbirding || touch /tmp/vbirding
那个 || 刚好完全跟 && 相反,当前一个指令有错误时,在 || 后面的指令才会被执行! (要注意,那个 | 是两个 | ,而 | 按键则是反斜线 \ 同一个按键, 因此,按下 [Shift] 加上 [\] 就会出现那个 | 啰!) 因此,简单的来说,当 ls /tmp/vbirding 发生错误时,才会使用 touch /tmp/vbirding 去建立这个档案的意思。 是否很有趣啊?这个 || 及 && 对于系统管理员在管理某些档案权限、存在等问题时, 可是很有用的东西喔!好了,现在我们来玩比较难一点的,看看底下的例题:
例题:以 ls 测试 /tmp/vbirding 是否存在,若存在则显示 "exist" ,若不存在,则显示 "not exist"!
这又牵涉到逻辑判断的问题,如果存在就显示某个数据,若不存在就显示其它数据,那我可以这样做:
ls /tmp/vbirding && echo "exist" || echo "not exist"
意思是说,当 ls /tmp/vbirding 执行后,若正确,就执行 echo "exist" ,若有问题,就执行 echo "not exist" !那如果我写成:
ls /tmp/vbirding || echo "not exist" && echo "exist"
对不对啊?这其实是有问题的,为什么呢?因为指令是一个一个往下执行,因此,在上面的例子当中,如果 /tmp/vbirding 不存在时,他会:
1. 若 ls /tmp/vbirding 不存在,因此回传一个非为 0 的数值;
2. 接下来经过 || 的判断,发现前一个指令回传非为 0 的数值,因此,程序开始执行 echo "not exist" ,而 echo "not exist" 程序肯定可以执行成功,因此会回传一个 0 值给后面的指令;
3. 经过 && 的判断,咦!是 0 啊!所以就开始执行 echo "exist" 。
所以啊,嘿嘿!第二个例子里面竟然会同时出现 not exist 与 exist 呢!真神奇~经过这个范例的练习,您应该会了解,由于指令是一个接着一个去执行的,因
此,如果真要使用判断, 那么这个 && 与 || 的顺序就不能搞错~一般来说,
判断式最多会有三个,也就是:
command1 && command2 || command3
而且顺序通常不会变,因为一般来说, command2 与 command3 会放置肯定可以执行成功的指令, 因此,依据上面例题的逻辑分析,您就会晓得为何要如此
放置啰~这很有用的啦!