linux 数据流重导向
linux 数据流重导向
拉普兰德 发表于2年前
linux 数据流重导向
  • 发表于 2年前
  • 阅读 6
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

什么是数据流重导向啊?这得要由命令的运行结果谈起!一般来说,如果你要运行一个命令,通常他会是这样的:

命令运行过程的数据传输情况
图 5.1.1、命令运行过程的数据传输情况

我们运行一个命令的时候,这个命令可能会由文件读入数据,经过处理之后,再将数据输出到屏幕上。 在上图当中, standard output 与 standard error output 分别代表『标准输出』与『标准错误输出』, 这两个玩意儿默认都是输出到屏幕上面来的啊!那么什么是标准输出与标准错误输出呢?


  • standard output 与 standard error output

简单的说,标准输出指的是『命令运行所回传的正确的信息』,而标准错误输出可理解为『 命令运行失败后,所回传的错误信息』。举个简单例子来说,我们的系统默认有 /etc/crontab 但却无 /etc/vbirdsay, 此时若下达『 cat /etc/crontab /etc/vbirdsay 』这个命令时,cat 会进行:

  • 标准输出:读取 /etc/crontab 后,将该文件内容显示到屏幕上;

  • 标准错误输出:因为无法找到 /etc/vbirdsay,因此在屏幕上显示错误信息

不管正确或错误的数据都是默认输出到屏幕上,所以屏幕当然是乱乱的!那能不能透过某些机制将这两股数据分开呢? 当然可以啊!那就是数据流重导向的功能啊!数据流重导向可以将 standard output (简称 stdout) 与 standard error output (简称 stderr) 分别传送到其他的文件或装置去,而分别传送所用的特殊字符则如下所示:

  1. 标准输入  (stdin) :代码为 0 ,使用 < 或 << ;

  2. 标准输出  (stdout):代码为 1 ,使用 > 或 >> ;

  3. 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;

为了理解 stdout 与 stderr ,我们先来进行一个范例的练习:

范例一:观察你的系统根目录 (/) 下各目录的文件名、权限与属性,并记录下来
[root@www ~]# ll /  <==此时屏幕会显示出文件名信息
[root@www ~]# ll / > ~/rootfile <==屏幕并无任何信息
[root@www ~]# ll  ~/rootfile <==有个新档被创建了!
-rw-r--r-- 1 root root 1089 Feb  6 17:00 /root/rootfile

怪了!屏幕怎么会完全没有数据呢?这是因为原本『 ll / 』所显示的数据已经被重新导向到 ~/rootfile 文件中了! 那个 ~/rootfile 的档名可以随便你取。如果你下达『 cat ~/rootfile 』那就可以看到原本应该在屏幕上面的数据啰。 如果我再次下达:『 ll /home > ~/rootfile 』后,那个 ~/rootfile 文件的内容变成什么? 他将变成『仅有 ll /home 的数据』而已!咦!原本的『 ll / 』数据就不见了吗?是的!因为该文件的创建方式是:

  1. 该文件 (本例中是 ~/rootfile) 若不存在,系统会自动的将他创建起来,但是

  2. 当这个文件存在的时候,那么系统就会先将这个文件内容清空,然后再将数据写入!

  3. 也就是若以 > 输出到一个已存在的文件中,那个文件就会被覆盖掉啰!

那如果我想要将数据累加而不想要将旧的数据删除,那该如何是好?利用两个大于的符号 (>>) 就好啦!以上面的范例来说,你应该要改成『 ll / >> ~/rootfile 』即可。 如此一来,当 (1) ~/rootfile 不存在时系统会主动创建这个文件;(2)若该文件已存在, 则数据会在该文件的最下方累加进去!

上面谈到的是 standard output 的正确数据,那如果是 standard error output 的错误数据呢?那就透过 2> 及 2>> 啰!同样是覆盖 (2>) 与累加 (2>>) 的特性!我们在刚刚才谈到 stdout 代码是 1 而 stderr 代码是 2 , 所以这个 2> 是很容易理解的,而如果仅存在 > 时,则代表默认的代码 1 啰!也就是说:

  • 1> :以覆盖的方法将『正确的数据』输出到指定的文件或装置上;

  • 1>>:以累加的方法将『正确的数据』输出到指定的文件或装置上;

  • 2> :以覆盖的方法将『错误的数据』输出到指定的文件或装置上;

  • 2>>:以累加的方法将『错误的数据』输出到指定的文件或装置上;

要注意喔,『 1>> 』以及『 2>> 』中间是没有空格的!OK!有些概念之后让我们继续聊一聊这家伙怎么应用吧! 当你以一般身份运行 find 这个命令的时候,由于权限的问题可能会产生一些错误信息。例如运行『 find / -name testing 』时,可能会产生类似『 find: /root: Permission denied 』之类的信息。 例如底下这个范例:

范例二:利用一般身份账号搜寻 /home 底下是否有名为 .bashrc 的文件存在
[root@www ~]# su - dmtsai  <==假设我的系统有名为 dmtsai 的账号
[dmtsai@www ~]$ find /home -name .bashrc <==身份是 dmtsai 喔!
find: /home/lost+found: Permission denied  <== Standard error
find: /home/alex: Permission denied        <== Standard error
find: /home/arod: Permission denied        <== Standard error
/home/dmtsai/.bashrc                       <== Standard output

由于 /home 底下还有我们之前创建的账号存在,那些账号的家目录你当然不能进入啊!所以就会有错误及正确数据了。 好了,那么假如我想要将数据输出到 list 这个文件中呢?运行『 find /home -name .bashrc > list 』 会有什么结果?呵呵,你会发现 list 里面存了刚刚那个『正确』的输出数据, 至于屏幕上还是会有错误的信息出现呢!伤脑筋!如果想要将正确的与错误的数据分别存入不同的文件中需要怎么做?

范例三:承范例二,将 stdout 与 stderr 分存到不同的文件去
[dmtsai@www ~]$ find /home -name .bashrc > list_right 2> list_error

注意喔,此时『屏幕上不会出现任何信息』!因为刚刚运行的结果中,有 Permission 的那几行错误信息都会跑到 list_error 这个文件中,至于正确的输出数据则会存到 list_right 这个文件中啰!这样可以了解了吗? 如果有点混乱的话,去休息一下再来看看吧!

命令运行的判断依据: ; , &&, ||

在某些情况下,很多命令我想要一次输入去运行,而不想要分次运行时,该如何是好?基本上你有两个选择, 一个是透过第十三章要介绍的 shell script 撰写脚本去运行,一种则是透过底下的介绍来一次输入多重命令喔!


  • cmd ; cmd (不考虑命令相关性的连续命令下达)

在某些时候,我们希望可以一次运行多个命令,例如在关机的时候我希望可以先运行两次 sync 同步化写入磁盘后才 shutdown 计算机,那么可以怎么作呢?这样做呀:

[root@www ~]# sync; sync; shutdown -h now

在命令与命令中间利用分号 (;) 来隔开,这样一来,分号前的命令运行完后就会立刻接着运行后面的命令了。 这真是方便啊~再来,换个角度来想,万一我想要在某个目录底下创建一个文件,也就是说,如果该目录存在的话, 那我才创建这个文件,如果不存在,那就算了。也就是说这两个命令彼此之间是有相关性的, 前一个命令是否成功的运行与后一个命令是否要运行有关!那就得动用到 && 或 || 啰!


  • $? (命令回传值) 与 && 或 ||

如同上面谈到的,两个命令之间有相依性,而这个相依性主要判断的地方就在于前一个命令运行的结果是否正确。 还记得本章之前我们曾介绍过命令回传值吧!嘿嘿!没错,您真聪明!就是透过这个回传值啦! 再复习一次『若前一个命令运行的结果为正确,在 Linux 底下会回传一个 $? = 0 的值』。 那么我们怎么透过这个回传值来判断后续的命令是否要运行呢?这就得要藉由『 && 』及『 || 』的帮忙了! 注意喔,两个 & 之间是没有空格的!那个 | 则是 [Shift]+[\] 的按键结果。

命令下达情况 说明
cmd1 && cmd2 1. 若 cmd1 运行完毕且正确运行($?=0),则开始运行 cmd2。
2. 若 cmd1 运行完毕且为错误 ($?≠0),则 cmd2 不运行。
cmd1 || cmd2 1. 若 cmd1 运行完毕且正确运行($?=0),则 cmd2 不运行。
2. 若 cmd1 运行完毕且为错误 ($?≠0),则开始运行 cmd2。

上述的 cmd1 及 cmd2 都是命令。好了,回到我们刚刚假想的情况,就是想要: (1)先判断一个目录是否存在; (2)若存在才在该目录底下创建一个文件。由于我们尚未介绍如何判断式 (test) 的使用,在这里我们使用 ls 以及回传值来判断目录是否存在啦! 让我们进行底下这个练习看看:

范例一:使用 ls 查阅目录 /tmp/abc 是否存在,若存在则用 touch 创建 /tmp/abc/hehe [root@www ~]# ls /tmp/abc && touch /tmp/abc/hehels: /tmp/abc: No such file or directory 
# ls 很干脆的说明找不到该目录,但并没有 touch 的错误,表示 touch 并没有运行[root@www ~]# mkdir /tmp/abc[root@www ~]# ls /tmp/abc && touch /tmp/abc/hehe[root@www ~]# ll /tmp/abc-rw-r--r-- 1 root root 0 Feb  7 12:43 hehe

看到了吧?如果 /tmp/abc 不存在时,touch 就不会被运行,若 /tmp/abc 存在的话,那么 touch 就会开始运行啰! 很不错用吧!不过,我们还得手动自行创建目录,伤脑筋~能不能自动判断,如果没有该目录就给予创建呢? 参考一下底下的例子先:

范例二:测试 /tmp/abc 是否存在,若不存在则予以创建,若存在就不作任何事情[root@www ~]# rm -r /tmp/abc                <==先删除此目录以方便测试[root@www ~]# ls /tmp/abc || mkdir /tmp/abcls: /tmp/abc: No such file or directory <==真的不存在喔![root@www ~]# ll /tmp/abc                  total 0                                 <==结果出现了!有进行 mkdir

如果你一再重复『 ls /tmp/abc || mkdir /tmp/abc 』画面也不会出现重复 mkdir 的错误!这是因为 /tmp/abc 已经存在, 所以后续的 mkdir 就不会进行!这样理解否?好了,让我们再次的讨论一下,如果我想要创建 /tmp/abc/hehe 这个文件, 但我并不知道 /tmp/abc 是否存在,那该如何是好?试看看:

范例三:我不清楚 /tmp/abc 是否存在,但就是要创建 /tmp/abc/hehe 文件[root@www ~]# ls /tmp/abc || mkdir /tmp/abc && touch /tmp/abc/hehe

上面这个范例三总是会创建 /tmp/abc/hehe 的喔!不论 /tmp/abc 是否存在。那么范例三应该如何解释呢? 由于Linux 底下的命令都是由左往右运行的,所以范例三有几种结果我们来分析一下:

  • (1)若 /tmp/abc 不存在故回传 $?≠0,则 (2)因为 || 遇到非为 0 的 $? 故开始 mkdir /tmp/abc,由于 mkdir /tmp/abc 会成功进行,所以回传 $?=0 (3)因为 && 遇到 $?=0 故会运行 touch /tmp/abc/hehe,最终 hehe 就被创建了;

  • (1)若 /tmp/abc 存在故回传 $?=0,则 (2)因为 || 遇到 0 的 $? 不会进行,此时 $?=0 继续向后传,故 (3)因为 && 遇到 $?=0 就开始创建 /tmp/abc/hehe 了!最终 /tmp/abc/hehe 被创建起来。

整个流程图示如下:

命令依序运行的关系示意图
图 5.2.1、 命令依序运行的关系示意图

上面这张图显示的两股数据中,上方的线段为不存在 /tmp/abc 时所进行的命令行为,下方的线段则是存在 /tmp/abc 所在的命令行为。如上所述,下方线段由于存在 /tmp/abc 所以导致 $?=0 ,让中间的 mkdir 就不运行了! 并将 $?=0 继续往后传给后续的 touch 去利用啦!瞭乎?在任何时刻你都可以拿上面这张图作为示意!


共有 人打赏支持
粉丝 2
博文 34
码字总数 50893
评论 (0)
×
拉普兰德
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: