简述
大家可能在shell脚本中经常看到类似于>/dev/null 2>&1这样的写法。
工作中可能直接拷贝现有的模板,使用了这样的写法,只知道这样做能够把标准输出和错误输出全都抛进/dev/null这个“黑洞”当中去,但是不明白为什么会有这样的写法,以及为什么需要这么写。这次我们就来简单探讨一下。
Shell重定向机制
在Shell中,它会处理我们从键盘上敲下字符产生的标准输入,并根据我们所敲入命令执行相应的程序或脚本,完成程序的执行并产生标准输出到屏幕(即控制台)或错误输出。但是我们并不是每一次都是以交互模式打开Shell,更多的情况下,我们是需要将程序的输出内容重定向到日志文件中,供我们回顾查询。
文件描述符Shell执行程序或脚本时,会默认打开3个文件描述符,每个描述符有对应的文件方便我们来使用:
| 类型 | 文件描述符 | 默认情况 | 对应文件句柄位置 |
|---|---|---|---|
| 标准输入(standard input) | 0 | 从键盘获取 | /proc/self/fd/0 |
| 标准输出(standard output) | 1 | 输出到控制台 | /proc/self/fd/1 |
| 错误输出(error output) | 2 | 输出到控制台 | /proc/self/fd/2 |
所以平时在Shell中敲命令,默认是从键盘上获取输入内容,并将结果返回到控制台(即屏幕)。但是这种在Shell中直接敲命令进行交互式操作运行程序的情况并不多见,所以我们需要利用重定向机制来满足我们的要求。
重定向
输出重定向
输出重定向的使用方法很简单,使用的方法如下:
| 命令格式 | 说明 |
|---|---|
| command > filename | 把标准输出重定向到新文件中 |
| command 1> filename | 同上 |
| command >> filename | 把标准输出追加到文件中 |
| command 1>> filename | 同上 |
| command 2> filename | 把标准错误重定向到新文件中 |
| command 2>> filename | 把标准错误追加到新文件中 |
重定向符号右边可以是一个文件,也可以是一个输出设备。因为默认的输出是标准输出,即输出到控制台(或者说屏幕上),所以如果是对标准输出做重定向可以省略重定向符号前的数字1(注意,重定向符号>或>>与数字1之间不能有空格)。
这里要注意两个重定向符号的一些区别:
>使用此重定向符号会判断右边的文件是否存在,如果存在的话会先删除,然后重新创建一个新的文件再输出;不存在的话则直接创建后再输出。>>使用此重定向符号则不会删除文件,会在原来的文件上进行追加;文件不存在也会创建新文件再追加输出。
下面我们通过一个简单的例子来体验一下:
我们创建一个简单的目录,并在目录下创建一个文件a.txt
接下来我们尝试执行命令ls a.txt b.txt > test.log,因为目录下仅存在a.txt文件,故仅能正常执行ls a.txt,对于另外一个文件b.txt是无法显示的,会产生错误输出。在控制台中就可以看到这样的输出
再看一下test.log文件中的内容
由此可见,单纯地使用>进行重定向,仅能将标准输出重定向到我们指定的文件中,对于错误输出是无能为力的,错误输出依然显示在了控制台当中。接下来我们再尝试修改一下命令,执行ls a.txt b.txt > ok.log 2> ng.log,可以看到控制台中没有任何输出回显
我们再查看一下ok.log和ng.log当中的内容
嗯,如我们所预想的一样,分别对标准输出和错误输出做了重定向,接下来,似乎只要简单地将标准输出和错误输出都重定向到同一个地方就可以了,我们再来试试看,执行ls a.txt b.txt > both.log 2> both.log
控制台上同样没有任何输出回显,貌似如我们预期的一样生效了,我们再来看一下both.log中的内容
看起来不错,标准输出有了,错误输出也有了,大功告成!但是细心的朋友会发现,错误输出的前面一小节内容似乎“丢掉了”一样,并没有如上面的ng.log文件内容完整。
没错,这里就是问题所在,类似于> logfile 2> logfile这样简单地叠加到文件中时,标准输出和错误输出会产生竞争,两者可能会互相覆盖输出内容,导致输出的内容不完整,如果使用这样的写法在生产环境里,万一被覆盖掉了关键的输出信息就不妙了。
重定向绑定
看到这里,我们再来回顾一下本文开始处介绍的>/dev/null 2>&1写法。将这条命令分解来看,有2部分:
>/dev/null
这条命令的作用是将标准输出1重定向到/dev/null设备上,这个设备代表了Linux的空设备文件,所有往这个设备里写入的内容都会丢失,俗称“黑洞”。这样做的话标准输出的产物就不复存在,没有任何地方能够找回这些产物。
2>&1
这条命令使用到了重定向绑定,采用>&即可将两个输出绑定在一起,就这个例子而言,它将错误输出2绑定到了标准输出1之上,也就是错误输出和标准输出都输出到同一个地方。
思考
既然这条命令的两个部分我们已经知道分别是什么作用了,现在思考一个问题,那么是否可以将这两个部分的顺序调换一下,改成2>&1 >/dev/null这种形式的话是不是效果还是一样的呢?答案是否定的,这样的更改会产生不同的结果。因为Linux在执行Shell脚本或者运行程序的时候,就会先确定好所有的输入输出位置,并且按照从左到右依次执行重定向的命令。那我们分析一下2>&1 >/dev/null代表了什么:
2>&1将错误输出2绑定到标准输出1上。由于此时标准输出仍然是默认值(即控制台),所以错误输出2会重定向到标准输出上(即控制台)
>/dev/null将标准输出1重定向到设备/dev/null上
就2>&1 >/dev/null的最终绑定的结果来看,可以参照下表来理解:
| 命令 | 标准输出1绑定目标 | 错误输出2绑定目标 |
|---|---|---|
| >/dev/null 2>&1 | /dev/null | /dev/null |
| 2>&1 >/dev/null | /dev/null | 1 |
输入重定向
说完输出重定向之后,我们再来探讨一下输入重定向,相比输出重定向而言,输入重定向要简单一点。基本的命令格式如下:
| 命令 | 说明 |
|---|---|
| command < filename | 以filename文件作为标准输入 |
| command 0< filename | 同上 |
| command << delimiter | 从标准输入中读入,直到遇到delimiter分隔符 |
同样的,如果输入重定向符号<左边没有写,即为默认输入0(键盘输入)
我们简单地举个栗子,执行cat命令,如果后面没有跟上任何文件名的话,它会以默认的标准输入(即键盘输入)作为输入源,并将输入内容回显
可以看到我们输入的内容原样回显在了标准输出中,按下键盘上的Ctrl + C即可终止程序。接下来我们试试从文件中读取内容并作为cat命令的输入,创建文件并使用重定向作为输入,例如cat < in.txt,效果如下:
我们在in.txt中随便写一些内容,可以看到在执行命令后将完整的文件内容输出到了控制台,这样就实现了输入重定向!
好,<的用法我们已经了解了,那么<<又是如何使用的呢?参考如下例子,执行 cat << end命令
可以看到,执行命令后我们也能正常输入内容,但敲下回车后并不会像cat命令一样立刻回显,而是会给出输入提示符并允许你一直输入内容,直到你单独键入一次end后再终止程序并回显之前所有的键盘输入内容。
注意!
end并不是固定的,这取决于你输入cat << end时<<输入重定向命令后面所跟的分割符是什么,如果你愿意,<<后面可以是任何你喜欢的单词、数字或符号(有些特殊符号不行,但是我没有全部试过,所以我也就不在此穷举了)
总结
本文主要介绍了Linux中输入输出重定向的使用方法和注意事项,实际工作中用到的最多的其实还是nohup commonad > xx.log 2>&1 &这种格式的写法,无脑用就完事了,如果有兴趣了解的话,可以再看看本文上述的内容,希望能帮助大家理解和加深记忆,谢谢~~
参考文档:https://www.cnblogs.com/youjianjiangnan/p/11561805.html










