Cover image

Linux管道与重定向

Oct 27, 2015

Shell里边的文件输入输出重定向什么的一直都是一知半解,只知道个>代表覆盖重写而>>代表对文件中追加,并不覆盖。 今天在用mplayer播放提示音希望不显示输出,从网上看到这个命令瞬间泪奔

$mplayer test.mp3 < /dev/null > /dev/null 2>1&

这到底什么个含义,决心花点时间来好好补补这一块的内容。

I/O 重定向

通常在Shell命令中最常见到三种输入输出类型,标准输入(stdin),标准输出(stdout)以及标准错误输出(stderr),感觉跟C/C++里边文件划分类似。 stdin一般指command接受的输入流,stdoutcommand正常输出情况,默认输出显示到终端,stderr则是command的错误输出,默认也是输出显示到终端。 而文件描述符则是与一个打开的文件或数据流相对应的一个整数,系统将0,1,2分别与标准输入,标准输出以及标准错误输出相对应。

输出重定向

重定向符号>以及>>意为输出重定向,包括标准输出以及标准错误输出,也是一般命令中比较常见的情况,与stdoutstderr两两组合就是四种情况

其中默认缺省表示标准输出,即>>>与前两个命令含义相同。

由于>命令会将存在的文件进行覆盖,我们可以通过set -o noclobber来进行相应的限制。设置成功后,如果再次进行重定向到已有的文件中就会报错,这时便 需要通过>|来进行命令。

$set -o noclobber
$ls
test1 test2 test3
$ls > test1
zsh: 文件已存在:test1
$ls >| test1
$cat test1
test1 test2 test3

另外就是&的使用,不过这里&并不是指将命令在后台运行。&>&>>可以将stdoutstderr重定义到同一文件中。这样就不用一个一个的指定了。 另一种方式这样2>&1,表示将stderr重定向到stdout的位置,即stdout输出到哪,stderr也输出到哪。“IBM developerWorks”上有个很不错的例子,

$command 2>&1 >output.txt
$command >output.txt 2>&1

这两个命令stderr重定向的位置并不相同。第一个先将stderr定向到stdout,而此时stdout的定向是默认的终端,所以stderr被定向到终端,之后 stdout被重定向到文件output中。第二条命令先将stdout定向到文件output,然后将stderr定向到stdout上,因此stderr最终被定向到文件中。

最后要提的是/dev/null这个空设备,被称为bit bucket或者黑洞,正如它名字所示,它会将一切写入到其中的数据流吞噬并丢弃,常用来将stdoutstderr 内容丢弃掉。但/dev/null是一个文件而不是目录,所以不能类似的将文件mv其中来删除。

关于tee命令,其实也有时候,我们希望输出不仅重定向到文件中,而且希望它能够在终端中显示出来,这样就不需要重定向后再继续打开文件来检查是否出问题。 这时候我们可以用到tee命令。man tee的解释是这样,从标准输入复制到每一个文件并复制到标准输出中。例如这样

$ls |tee file1 file2
test1 test2 test3
$cat file1
test1 test2 test3
$cat file2
test1 test2 test3

而在一般的Linux tricks 有时候会利用tee来帮忙解决vim时忘记加sudo。可以这样子使用:w !sudo tee %。其中:w将更改后的文件作为标准输出传递给tee, 而%代表当前文件名,然后利用sudo权限把标准输出传到当前文件中去。

输入重定向

输入重定向自己平常到用的不多,可以用<来接受文件内容来作为标准输入。

<<则通常与一个单词来构成一个结束标识符来接受终端的输入。如

$sort <<END
heredoc>2
heredoc>1
heredoc>3
heredoc>END
1
2
3

管道

相对与重定向,管道就没那么复杂了。一般来讲可以用|来将上一个命令的stdout作为当前命令的stdin来使用。但需要注意的是,stderr是不会被处理的。

$command1 |command2 |command3

关于xargsxargs比较牛的地方是可以将标准输入来作为命令的参数传到命令中,而管道传给命令的只能是命令接受的stdinxargs通常与find连用来查找文件。 记忆中自己用到xargs的地方是在Redis。由于Redis删除key的时候不支持通配符,而上万条的数据又不能一个一个手动删除,所以可以利用xargs

$redis-cli -n 0 keys 'keys:*' |xargs redis-cli -n 0 del

先用keys命令找到数据库中所有匹配数据,然后通过xargs将输出作为del的参数来进行删除。

参考资料:学习Linux,101:流,管道,重定向


27 Oct 2015

Post by: MetaCoder