0%

Linux命令中的重定向(>)和管道(|)

在日常使用Linux命令时候,经常使用重定向或者管道的方式处理命令的结果。以前对这两个命令的使用场景存在一些困惑,所以本文对这两个命令进行详细的总结。

文件描述符

Linux的宗旨是一切皆文件,对于进程、IO等等都是通过文件的形式存在,这些文件都通过文件描述符的形式来表示。Linux的文件描述符可以理解为Linux为了跟踪一个打开的文件而分配的唯一标号,可以通过这个标号对文件实现读写操作。Linux系统的文件描述符一般都有最大的限制,可以通过ulimit -n这条命令来查看。如果想改变这个限制,需要修改/etc/security/limits.conf 文件,如下:

1
2
3
vi /etc/security/limits.conf
* hard nofile 102400
* soft nofile 102400

保存后退出,重新登录,可以看到其被改变。

我们知道,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
    2
    ls: 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
    2
    ls exists.txt no-exists.txt 2 > /dev/null
    ls exists.txt no-exists.txt 2 > &-

    /dev/null 这个设备,是linux 中黑洞设备,什么信息只要输出给这个设备,都会给吃掉。&代表当前命令进程中是已经存在的文件描述符,&1代表标准输出,因为1可以省略,所以&也代表标准输出,&2代表标准错误,&-代表关闭与它绑定的描述符。重定向符号后面的文件描述符用&引用。

  • 关闭所有输出

    和上面类似:

    1
    2
    ls 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
2
3
exec 6 > &1
exec 1 > success.txt
exec 1 > &6

上述命令分别表示:

  • 将标准输出与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重定向区别