详解Linux--标准输入输出、重定向及管道运用

概述

当我们在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。由于文件描述符不容易记忆,shell同时也给出了相应的文件名。


下面就是这些文件描述符及它们通常所对应的文件名:

文件文件描述符

系统中实际上有12个文件描述符,但是正如我们在上表中所看到的, 0、1、2是标准输入、输出和错误。可以任意使用文件描述符3到9。

详解Linux--标准输入输出、重定向及管道运用

标准输入是文件描述符0。它是命令的输入,缺省是键盘,也可以是文件或其他命令的输出。

标准输出是文件描述符1。它是命令的输出,缺省是屏幕,也可以是文件。

标准错误是文件描述符2。这是命令错误的输出,缺省是屏幕,同样也可以是文件。


文件重定向

在执行命令时,可以指定命令的标准输入、输出和错误,要实现这一点就需要使用文件重定向。下面列出了最常用的重定向组合,并给出了相应的文件描述符。

在对标准错误进行重定向时,必须要使用文件描述符,但是对于标准输入和输出来说,这不是必须的。

command << delimiter 把从标准输入中读入,直至遇到delimiter分界符

command <&m 把把文件描述符m作为标准输入

command >&m 把把标准输出重定向到文件描述符m中

command <&- 把关闭标准输入 //?这个“-”代表什么意思

详解Linux--标准输入输出、重定向及管道运用


重定向标准输出

在下面的命令中,把/etc/passwd文件中的用户ID域按照用户命排列。

该命令的输出重定向到sort.out文件中。要提醒注意的是,在使用sort命令的时候(或其他含有相似输入文件参数的命令),重定向符号一定要离开sort命令两个空格,否则该命令会把它当作输入文件。

$ cat passwd | awk -F: '{print $1}' | sort 1>sort.out //错误信息不会输出到sort.out

也可以使用如下的表达方式,结果和上面一样:

$ cat passwd | awk -F: '{print $1}' | sort >sort.out //与上面的等价,什么也不写的>,默认就是1【标准输出】


重定向标准输入

可以指定命令的标准输入。在awk一章就会遇到这样的情况。下面给出一个这样的例子:

$ sort < name.txt

在上面的命令中, sort命令的输入是采用重定向的方式给出的,不过也可以直接把相应的

文件作为该命令的参数:

$ sort name.txt

在上面的例子中,还可以更进一步地通过重定向为sort命令指定一个输出文件name.out。

这样屏幕上将不会出现任何信息(除了错误信息以外):

$ sort <name.txt >name.out

实例说明:

cat >> myfile <<hwb //直到遇到字符串“hwb”,会停止输入
> Hello there I am using a $TERIM terminal 
> and my user name is $LOGNAME 
> bye
> test
> hwb

详解Linux--标准输入输出、重定向及管道运用


重定向标准错误

为了重定向标准错误,可以指定文件描述符2。

grep命令没有找到该文件,缺省地向终端输出了一个错误信息。现在让我们把错误重定向到文件/dev/null中(实际就上是系统的垃圾箱):

$ grep "abc" test 2>/dev/null

详解Linux--标准输入输出、重定向及管道运用

这样所有的错误输出都输送到了/dev/null,不再出现在屏幕上。


合并标准输出和标准错误

在合并标准输出和标准错误的时候,切记shell是从左至右分析相应的命令的。下面给出一个例子:

$ echo test >test.out 2>&1

详解Linux--标准输入输出、重定向及管道运用

在上面的例子中,我们将test脚本的输出重定向到>test.out文件中,而且其错误也被重定向到相同的文件中。

shell中可能经常能看到:>/dev/null 2>&1 //即忽略所有输出信息。


复杂案例

需求:命令 cmd1, cmd2, cmd3, cmd4. 如何利用单向管道完成下列功能:

1. 所有命令并行执行。
2. cmd1 和 cmd2 不需要 stdin。
3. cmd1 和 cmd2 的 stdout 定向到 cmd3 的 stdin。
4. cmd1 和 cmd2 的 stderr 定向到 cmd4 的 stdin。
5. cmd3 的 stdout 定向到文件 a, stderr 定向到屏幕。
6. cmd4 的 stdout 定向到文件 b, stderr 定向到屏幕。
7. cmd1 的返回码赋给变量 s。
8. 不能利用临时文件。

解决方法:

exec 3>&1; exec 4>&1 
s=$(((((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>
&1 | cmd3 >a 2>&3 ) 2>&1 | cmd4 >b ) 4>&1) 
exec 3>&-; exec 4>&-

说明:

exec 3>&1; exec 4>&1就是建立FD3 ,给cmd1恢复其FD1用和给cmd3 恢复其FD2用,建立FD4,保存“echo $?”输出值的“草稿纸”。

第一对括号:(cmd1 1>&3 ; echo $? >&4 ) 和其后(第一个)管道。在第一个括号(子shell)中,其FD1已经连到 管道中了,所以用 FD3 将 FD1恢复正常,不让他往管道跑;这里的cmd1没有stdin,接着将 cmd1 运行的返回码 保存到 FD4 中。

第二对括号:((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>&1 和其后(第二个)管道。前面的 FD1 已经不送给 cmd2了,FD2 默认也不送过来,所以cmd2 也没有stdin ,所以在第二对括号里面:cmd1和cmd2 的stdout、stderr 为默认输出,一直遇到 “3>&1”为止。请注意:“3>&1”,先将第二对括号看出一个命令,他们遇到 第二个管道时,其FD1 连到 管道 “|”,由于“3>&1”的作用,子shell的FD1 送给FD3 使用,所以所有FD3 的输出都 “流往”cmd3,又由于继承关系(继承第一行的命令),FD3实际上就是cmd1和cmd2的stdout,于是“ cmd1 和 cmd2 的 stdout 定向到 cmd3 的 stdin”

第 三对括号:(((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>&1 | cmd3 >a 2>&3 ) 2>&1 和其后的第三个管道。cmd1 和 cmd2 的 stdout 已经定向到 cmd3 的 stdin,处理之后,cmd3 >a 意味着将其 stdout 送给 a 文件。而2>&3的意思是:恢复cmd3的错误输出为FD3,即送往 monitor。于是“cmd3 的 stdout 定向到文件 a, stderr 定向到屏幕”。如果没有“2>&3”,那么cmd3的错误输出就会干扰cmd1和cmd2的错误输出,所以它是必须的!请注意第三对括号后 的 “2>&1”| ,其子shell的FD1 本来连接着管道“|”,但子shell FD1 慷慨大方,送给了 FD2,于是FD2 连接着管道。还记得前面的 cmd1 和 cmd2 吗?他们的stderr一直没动了。于是在这里,通过管道送给了 第四个命令cmd4 了。即“cmd1 和 cmd2 的 stderr 定向到 cmd4 的 stdin”。后面就比较简单了。cmd4 >b 表示“cmd4 的 stdout 定向到文件 b, stderr 定向到屏幕(默认)”

第 四对括号:((((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>&1 | cmd3 >a 2>&3 ) 2>&1 | cmd4 >b ) 与其后的 4>&1。四对括号里面的 FD1、FD2都处理完了。但是还记得前面“echo $? >&4”那块“草稿纸”吗?“4>&1”的作用就是“将草稿纸上的内容送给monitor”,但是由于最外面还有 $() 将其“包着”。于是其值赋给变量“s”。


详解Linux--标准输入输出、重定向及管道运用

重定向的使用有如下规律:

1)标准输入0、输出1、错误2需要分别重定向,一个重定向只能改变它们中的一个。

2)标准输入0和标准输出1可以省略。(当其出现重定向符号左侧时)

3)文件描述符在重定向符号左侧时直接写即可,在右侧时前面加& 【类似于指针前要加*号,来区分这、两种情况】。

4)文件描述符与重定向符号之间不能有空格

后面会分享更多关于devops和DBA方面内容,大家可以关注下~

详解Linux--标准输入输出、重定向及管道运用

相关推荐