详解shell中的几种标准输出重定向方式

shell重定向介绍

无论是用什麽语言开发的程序,都会处理外部的输入,然后将运算结果输出到指定的位置。在交互式的程序中,输入来自用户的键盘和鼠标,结果输出到用户的屏幕,甚至到其他播放设备中。而对于某些后台运行的程序,输入可能来自于外部的一些文件,运算的结果通常又需要写到其他的文件中。比如:程序在运行的过程中,会有一些关键性的信息,比如异常堆栈,外部接口调用情况等,这些都会统统写到日志文件里。
shell脚本也一样,但是我们一般在使用shell命令的时候,更多地还是通过键盘输入,然后直接在屏幕上查看命令的执行结果。如果某些情况下,我们需要将shell命令的执行结果存储到具体文件中,那么我们就需要使用shell中输入/输出的重定向功能。

文件描述符

当执行shell命令时,linux系统会默认打开3个文件,每个文件有对应的文件描述符来方便我们使用:

文件描述符 代表的含义 默认情况 对应文件句柄位置
1 标准正确输出(standard output) 输出到屏幕(即控制台) /proc/self/fd/1
2 标准错误输出(error output) 输出到屏幕(即控制台) /proc/self/fd/2
0 标准输入(standard input) 从键盘获得输入 /proc/self/fd/0

所以我们平时在执行shell命令中,都默认是从键盘获得输入,并且将结果输出到控制台上。但是我们可以通过更改文件描述符默认的指向,从而实现输入输出的重定向。比如我们将1指向文件,那么标准的输出就会输出到文件中。

输出重定向

命令格式 代表的含义
command > filename 把标准正确输出重定向到指定文件中
command 1 > filename 同上
command >> filename 把标准正确输出追加到指定文件中
command 1 >> filename 同上
command 2 > filename 把标准错误输出重定向到指定文件中
command 2 >> filename 同上
  1. “>” 或者 “>>” 符号表示对标准输出(包括正确的和错误的输出)进行重定向。
  2. 这两个符号的左边表示文件描述符,如果没有的话表示1,也就是标准正确输出,符号的右边可以是一个文件,也可以是一个输出设备。
  3. 当使用">" 符号时,会判断右边的文件存不存在,如果存在的话就先删除,然后创建一个新的文件,不存在的话则直接创建。当使用">" 符号时,会判断右边的文件存不存在,如果存在的话就先删除,然后创建一个新的文件,不存在的话则直接创建。
  4. 当使用 “>>” 符号进行追加时,则不会删除原来已经存在的文件。当使用 “>>” 符号进行追加时,则不会删除原来已经存在的文件。
默认情况下:是将标准正确/错误输出—打印到控制台(即屏幕上)
[root@myhost ~]# tree /home/test/
/home/test/
└── a.txt

0 directories, 1 file

[root@myhost ~]# ll /home/test/a.txt  /home/test/b.txt 
ll: cannot access /home/test/b.txt: No such file or directory
-rw-r--r-- 1 root root 0 Jan  4 14:55 /home/test/a.txt

#在我们执行 ls /home/test/a.txt  /home/test/b.txt  命令后,一共有两种输出,其中 
ll: cannot access /home/test/b.txt: No such file or directory是标准错误输出;
-rw-r--r-- 1 root root 0 Jan  4 14:55 /home/test/a.txt 是标准正确输出。
把标准正确输出重定向到指定文件中,标准错误默认情况
[root@myhost ~]# tree /home/test/
/home/test/
└── a.txt

0 directories, 1 file
[root@myhost ~]# ll /home/test/a.txt  /home/test/b.txt > /home/test/default.log
ls: cannot access /home/test/b.txt: No such file or directory

[root@myhost ~]# cat /home/test/default.log 
-rw-r--r-- 1 root root 0 Jan  4 14:55 /home/test/a.txt

-------------------------------------------------------------------------------------------------------------
[root@myhost ~]# tree /home/test/
/home/test/
└── a.txt

0 directories, 1 file

[root@myhost  ~]#  ll /home/test/a.txt  /home/test/b.txt 1> /home/test/1.log
ls: cannot access /home/test/b.txt: No such file or directory

[root@myhost  ~]# cat /home/test/1.log
-rw-r--r-- 1 root root 0 Jan  4 14:55 /home/test/a.txt
把标准错误输出重定向到指定文件中,标准正确默认情况
[root@myhost ~]# tree /home/test/
/home/test/
└── a.txt

0 directories, 1 file

[root@myhost ~]# ll /home/test/a.txt  /home/test/b.txt 2> /home/test/2.log
-rw-r--r-- 1 root root 0 Jan  4 14:55 /home/test/a.txt

[root@myhost ~]# cat /home/test/2.log 
ls: cannot access /home/test/b.txt: No such file or directory
把标准正确输出重定向到某个指定文件中,标准错误输出重定向到另外一个指定文件中
[root@myhost ~]# tree /home/test/
/home/test/
└── a.txt

0 directories, 1 file

[root@myhost ~]#  ll /home/test/a.txt  /home/test/b.txt   1>/home/test/record-1.log   2>/home/test/record-2.log

[root@myhost ~]# cat /home/test/record-1.log
-rw-r--r-- 1 root root 0 Jan  4 14:55 /home/test/a.txt
[root@myhost ~]# cat /home/test/record-2.log
ls: cannot access /home/test/b.txt: No such file or directory
把标准正确输出 和 标准错误输出 同时重定向到一个指定文件或者指定设备中

输入重定向

命令格式 代表的含义
command < filename 以filename文件作为标准输入
command 0 < filename 同上
command << filename 以filename文件作为标准输入
command 0 << filename 同上
command <<delimiter 从标准输入中读入,直到遇到delimiter分隔符

我们使用"<" 和 “<<” 符号对输入做重定向,如果符号左边没有写值,那么默认就是0。
现在我们以cat命令为例,如果cat后面没有跟文件名的话,那它的作用就是在输将标准输入(比如键盘)之后按下enter键然后将内容回显到标准输出(比如屏幕)上:

[root@myhost ~]# cat

123  #这一排是我输入的
123  #这一排是回显的
456  #这一排是我输入的
456  #这一排是回显的
789  #这一排是我输入的
789  #这一排是回显的

我们可以将利用输入重定向,将我们在键盘上敲入的字符写入到文件中。我们需要使用ctrl+c来结束输入:

[root@myhost ~]# cat  >/home/test/record-to.log
123
test
^C
# cat /home/test/record-to.log
123
test

好了,此时我们觉得自己在键盘上敲比较累,还是直接让cat读取一个文件吧。那么我们需要利用输入重定向:

[root@myhost ~]# touch /home/test/record-show.log
[root@myhost ~]# cat >/home/test/record-show.log
123oldcontent
^C
[root@myhost ~]# cat  /home/test/record-show.log
123oldcontent
---------------------------------------------------------------------------------------------------------------------
[root@myhost ~]# touch /home/test/record-new.log
[root@myhost ~]# cat > /home/test/record-new.log <  /home/test/record-show.log
[root@myhost ~]# cat  /home/test/record-new.log 
123oldcontent

神奇的事情发生了,/home/test/record-new.log 文件里面的内容被替换成了/home/test/record-show.log 文件里的内容。那麽同理 << 的作用是 将/home/test/record-show.log 的内容追加到/home/test/record-new.log里面。
我们看到,当我们输入完cat > /home/test/record-to.log,然后敲下回车之后,命令并没有结束,此时cat命令像一开始一样,等待你给它输入数据。然后当我们敲入<< end (即cat >/home/test/record-to.log << end)之后,cat命令就结束了。end之前输入的字符都已经被写入到了/home/test/record-to.log 文件中。这就是输入分割符的作用。

高级用法

1. 重定向绑定(>/dev/null 2>&1)

>/dev/null

这条命令等同于 1>/dev/null,作用是将标准正确输出1重定向到/dev/null中。 /dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。那么执行了>/dev/null之后,标准正确输出的内容就会不再存在,没有任何地方能够找到输出的内容。

2>&1

这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。这条命令的作用是将标准错误输出和标准正确输出输出同用一个文件描述符,说人话就是标准错误输出和标准正确输出重定向到同一个地方。
linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令,所以>/dev/null 2>&1的作用就是让标准正确输出重定向到/dev/null中(丢弃标准正确输出),然后标准错误输出由于重用了标准正确输出的描述符,所以标准错误输出也会被定向到了/dev/null中,标准错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。

2. 比较一下(>/dev/null 2>&1 和 2>&1 >/dev/null)

上面提到了,linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令。那么我们同样从左到右地来分析2>&1 >/dev/null:

  1. “2>&1”,将标准错误输出绑定到标准正确输出上。由于此时的标准正确输出是默认值,也就是重定向到屏幕上,所以此时标准错误输出会重定向到屏幕上。
  2. “>/dev/null”,将标准正确输出1重定向到/dev/null中。

结论:

命令 标准正确输出 标准错误输出
>/dev/null 2>&1 丢弃 丢弃
2>&1 >/dev/null 丢弃 屏幕

3. 比较一下(>/dev/null 2>&1 和 >/dev/null 2>/dev/null)

为什麽一定要用重定向绑定,而不是像>/dev/null 2>/dev/null这样子分开来一遍呢?现在我们尝试将标准正确输出和标准错误输出都定向到/home/test/record-to.log文件中:

[root@myhost ~]# tree /home/test/
/home/test/
└── a.txt

0 directories, 1 file

[root@myhost ~]# ll /home/test/a.txt  /home/test/b.txt 1>/home/test/record-to.log  2>/home/test/record-to.log

[root@iZbp17qpn8m91l80gz044sZ test]# cat /home/test/record-to.log
-rw-r--r-- 1 root root 0 Jan  4 14:55 /home/test/a.txt
ectory  #这一行不正常

我们看到指定文件接收到额内容并不正常,这是为啥呢?这是因为采用这种写法,标准正确输出和标准错误输出会抢占往/home/test/record-to.log文件的管道,所以可能会导致输出内容的时候出现缺失、覆盖等情况。现在是出现了乱码,有时候也有可能出现只有error信息或者只有正常信息的情况。不管怎么说,采用这种写法,最后的情况是无法预估的。
而且,由于/home/test/record-to.log文件被打开了两次,两个文件描述符会抢占往文件中输出内容,从整体IO效率来讲这种写法也不如>/dev/null 2>&1来得高。

4. 与nohup结合使用

我们经常使用nohup command &命令形式来启动一些后台程序,比如一些java服务:

[root@myhost ~]# nohup java -jar xxxx.jar &

为了不让一些执行信息输出到屏幕上(控制台),我们还会加上刚才提到的>/dev/null 2>&1命令来丢弃所有的输出:

[root@myhost ~]# nohup java -jar xxxx.jar >/dev/null 2>&1 &

猜你喜欢

转载自blog.csdn.net/yuki5233/article/details/85091314
今日推荐