详解Linux--标准输入输出、重定向及管道运用
概述
当我们在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。由于文件描述符不容易记忆,shell同时也给出了相应的文件名。
下面就是这些文件描述符及它们通常所对应的文件名:
文件文件描述符
系统中实际上有12个文件描述符,但是正如我们在上表中所看到的, 0、1、2是标准输入、输出和错误。可以任意使用文件描述符3到9。
标准输入是文件描述符0。它是命令的输入,缺省是键盘,也可以是文件或其他命令的输出。
标准输出是文件描述符1。它是命令的输出,缺省是屏幕,也可以是文件。
标准错误是文件描述符2。这是命令错误的输出,缺省是屏幕,同样也可以是文件。
文件重定向
在执行命令时,可以指定命令的标准输入、输出和错误,要实现这一点就需要使用文件重定向。下面列出了最常用的重定向组合,并给出了相应的文件描述符。
在对标准错误进行重定向时,必须要使用文件描述符,但是对于标准输入和输出来说,这不是必须的。
command << delimiter 把从标准输入中读入,直至遇到delimiter分界符
command <&m 把把文件描述符m作为标准输入
command >&m 把把标准输出重定向到文件描述符m中
command <&- 把关闭标准输入 //?这个“-”代表什么意思
重定向标准输出
在下面的命令中,把/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
重定向标准错误
为了重定向标准错误,可以指定文件描述符2。
grep命令没有找到该文件,缺省地向终端输出了一个错误信息。现在让我们把错误重定向到文件/dev/null中(实际就上是系统的垃圾箱):
$ grep "abc" test 2>/dev/null
这样所有的错误输出都输送到了/dev/null,不再出现在屏幕上。
合并标准输出和标准错误
在合并标准输出和标准错误的时候,切记shell是从左至右分析相应的命令的。下面给出一个例子:
$ echo test >test.out 2>&1
在上面的例子中,我们将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”。
重定向的使用有如下规律:
1)标准输入0、输出1、错误2需要分别重定向,一个重定向只能改变它们中的一个。
2)标准输入0和标准输出1可以省略。(当其出现重定向符号左侧时)
3)文件描述符在重定向符号左侧时直接写即可,在右侧时前面加& 【类似于指针前要加*号,来区分这、两种情况】。
4)文件描述符与重定向符号之间不能有空格
后面会分享更多关于devops和DBA方面内容,大家可以关注下~