在日常使用Linux命令时候,经常使用重定向或者管道的方式处理命令的结果。以前对这两个命令的使用场景存在一些困惑,所以本文对这两个命令进行详细的总结。
文件描述符
Linux的宗旨是一切皆文件,对于进程、IO等等都是通过文件的形式存在,这些文件都通过文件描述符的形式来表示。Linux的文件描述符可以理解为Linux为了跟踪一个打开的文件而分配的唯一标号,可以通过这个标号对文件实现读写操作。Linux系统的文件描述符一般都有最大的限制,可以通过ulimit -n
这条命令来查看。如果想改变这个限制,需要修改/etc/security/limits.conf 文件,如下:
1 | vi /etc/security/limits.conf |
保存后退出,重新登录,可以看到其被改变。
我们知道,Linux启动时,最开始会创建init进程,其余的程序都是这个进程的子进程。而init进程默认打开3个文件描述符:
- 标准输入:standard input(0)
- 标准输出:standard output(1)
- 标准错误:standard error(2)
其中标准输入就是键盘,标准输出和标准错误都是屏幕,后面的数字分别是他们的文件描述符。
对于每个Linux进程,其都是init的子进程,包括bash命令窗口,而其中执行的shell命令,则更是如此。我们又知道,在Linux中,子进程会继承父进程的文件描述符,所以说,Linux中每个程序,执行的每个shell命令,拥有这三个文件描述符,而程序后续打开的文件,其文件描述符则(从3开始)依次增加。
对于一条shell命令,其从标准输入(键盘)中获得输入,如果执行成功,则将输出打印在标准输出(屏幕)上;如果执行出错,将结果打印在标准错误(屏幕)上。
关于文件描述符有很多相关知识,本文不展开,大家只要知道这个概念即可。
重定向
根据前文,我们知道了命令的输入输出默认的文件描述符,如果我们不使用默认的文件描述符,而想自己指定,则需要用到重定向了。
输出重定向
输出重定向是最常见的了,格式一般如下:
1 | command [1-n] > file或者文件描述符或者设备 |
下面示例输出重定向的操作,假设当前目录下只存在一个文件exists.txt。
不重定向输出
执行命令:
1
ls exists.txt no-exists.txt
因为exists.txt文件存在,而no-exists.txt文件不存在,因此这个命令即具有成功执行的结果(标准输出),又具有出错的结果(标准错误)。由于这个命令没有进行重定向,因此标准输出和标准错误都将打印在屏幕上:
1
2ls: no-exists.txt: No such file or directory # 执行错误,标准错误
exists.txt # 成功执行,标准输出重定向输出
执行命令:
1
ls exists.txt no-exists.txt 1 > success.txt 2 > fail.txt
命令执行,屏幕上将不显示任何信息,但是多了两个文件,其中succcess.txt中是执行成功的结果,标准输出重定向的文件,内容为
exists.txt
,而fail.txt是执行出错的结果,标准错误重定向的结果,内容为ls: no-exists.txt: No such file or directory
。只重定向标准输出
执行命令:
1
ls exists.txt no-exists.txt > result.txt
这个应该是我们平时用的最多的形式了,其意义就是将命令执行成功的结果输出到result.txt中,因此屏幕上没有命令执行成功的结果,只有出错的结果。值得注意的是,标准输出的文件描述符1可以省略
同理,我们也可以只重定向标准错误的结果,不过文件描述符2不能省略。
关闭标准错误
将标准错误信息关闭掉:
1
2ls exists.txt no-exists.txt 2 > /dev/null
ls exists.txt no-exists.txt 2 > &-/dev/null 这个设备,是linux 中黑洞设备,什么信息只要输出给这个设备,都会给吃掉。&
代表当前命令进程中是已经存在的文件描述符,&1代表标准输出,因为1可以省略,所以&也代表标准输出,&2代表标准错误,&-代表关闭与它绑定的描述符。重定向符号后面的文件描述符用&引用。 关闭所有输出
和上面类似:
1
2ls exists.txt no-exists.txt 1 > /dev/null 2 > /dev/null
ls exists.txt no-exists.txt 1 > &- 2 > &-将标准错误绑定给标准输出
1
ls exists.txt no-exists.txt 2 > &1 1 > /dev/null
将标准错误绑定给标准输出,然后将标准输出发送给/dev/null设备,常用
重定向输出还存在以下需要注意的地方:
* shell遇到`>`操作符,会判断右边文件是否存在,如果存在就先删除,并且创建新文件。不存在直接创建。 无论左边命令执行是否成功。右边文件都会存在。
* `>>`操作符,判断右边文件,如果不存在,先创建。如果存在,以添加方式打开文件,会分配一个文件描述符[不特别指定,默认为1,2]然后,与左边的标准输出(1)或标准错误(2) 绑定。
* 一条命令在执行前,先会检查输出是否正确,如果输出设备错误,将不会进行命令执行
输入重定向
输入重定向和输出重定向类似,其格式为:
1 | command [n] < file或文件描述符或者设备 |
示例:
1 | cat > output.txt < input.txt |
这条命令cat命令的输入重定向到input.txt文件,因此该文件的内容作为cat的输入。然后cat命令的输出重定向到output.txt,因此将内容输出到output.txt中。
与输出重定向类似,输入重定向的<<
也表示追加。
绑定重定向
上面的输出和输出绑定的文件或者设备只对该命令有效,如果需要一次绑定,接下来均有效的话,可以使用exec命令来绑定描述符。
1 | exec 6 > &1 |
上述命令分别表示:
将标准输出与fd 6绑定。
将标准输出重定向到success.txt,接下来的指令执行成功的结果将不在屏幕上显示。
恢复标准输出。
说明:使用前先将标准输入保存到文件描述符6,这里说明下,文件描述符默认会打开0,1,2,还可以使用自定义描述符。然后对标准输出绑定到文件,接下来所有输出都会发生到文件。使用完后,恢复标准的输出,关闭打开文件描述符6。
管道
管道的符号是|
,它仅能处理经由前面一个指令传出的正确输出信息,也就是标准输出(standard output)的信息,对于标准错误(stdandard error)信息没有直接处理能力。然后,传递给下一个命令,作为其标准输入(standard input)。因此可以认为管道其实是重定向的一种常用形式。
注意:
管道命令只处理前一个命令正确输出,不处理错误输出
管道命令右边命令,必须能够接收标准输入流命令才行。
使用示例:
1 | cat test.txt | grep -n 'test' |
cat test.txt
会将test.txt的内容作为标准输出,然后利用管道,将其作为grep -n 'test'
命令的标准输入。
管道和重定向的区别
管道触发两个子进程,执行
|
两边的程序;而重定向是在一个进程内执行。管道两边都是shell命令
重定向符号的右边只能是Linux文件(普通文件,文件描述符,文件设备)
重定向符号的优先级大于管道
关于Linux重定向和管道的更多用法,可以参考:linux shell 管道命令(pipe)使用及与shell重定向区别