您的位置:首页 > 运维架构 > Linux

Linux管道只传到错误信息(stderr)——从一个小问题写起

2014-10-09 00:00 1051 查看
摘要: 对shell输出的错误信息进行过滤处理,使用管道处理标准错误信息(stderr)

碰到问题

在自己编写的一个小脚本中,碰到这个一个小问题:

问题背景:

在脚本中多次导出数据,为了不用重复输入密码,在运行时,使用read读取用户输入,将密码存入变量。将mysql的命令中使用该变量,此时mysql提示在命令行直接输入密码不安全(其实他人无法看到密码):

# 脚本读取使用密码的一段为:
read -s -p "Enter Password: " passwd
mysql -uroot -S xxx -p$passwd -e "xxx" > xxx.sql

使用该脚本时,MySQL的提示为:

Enter password:
Warning: Using a password on the command line interface can be insecure.

第二行的提示其实没啥用,但是又不愿意使用 2>/dev/null 把所有错误信息都屏蔽掉。强迫症表示要干掉这条错误信息!

问题内容:

将标准输出(stdout),即导出的数据,重定向到文件保存。将输出的错误信息(stderr)中提示在命令行中使用密码不安全的Warning过滤掉(程序员只关心error,无视warning :) ),同时,不影响其他错误信息的输出(不被过滤掉,也不会混到导出的数据中)。

求解过程

在Linux的bash中,可以将命令的标准输入(stdin)、标准输出(stdout)、标准错误输出(stderr)通过数据流重导向导入到文件,这可以方便我们记录很多信息。

管道的短处

在Linux中,管道”|“是经常使用的命令。可以很方便地利用一个命令来处理上一个命令的输出,准确的说是可以处理上一个命令的标准输出(stdout),管道并不能处理上一个命令的错误输出信息(stderr),如下:

# /tmp 目录下存在两个t开头的文件,不存在名为fff和sss的文件
# 这条命令的输出中包含两行标准输出(stdout)和两行标准错误输出(stderr)
[root /tmp ]$ ll t* fff sss
ls: cannot access fff: No such file or directory
ls: cannot access sss: No such file or directory
-rw-r--r-- 1 root root  29 Oct  8 15:11 t
-rwxr--r-- 1 root root 291 Sep 28 18:00 t.sh

# 无论grep过滤的关键词是什么,错误输出(stderr)的两行都在
[root /tmp ]$ ll t* fff sss | grep "fff"
ls: cannot access fff: No such file or directory
ls: cannot access sss: No such file or directory

[root /tmp ]$ ll t* fff sss | grep "t.sh"
ls: cannot access fff: No such file or directory
ls: cannot access sss: No such file or directory
-rwxr--r-- 1 root root 291 Sep 28 18:00 t.sh


数据流重导向和管道

当然,我们可以使用 2>&1 的数据流重定向,将错误输出(stderr)导向标准输出,这里很多人都知道:

# 在命令后加上 2>&1 就能使grep也能够过滤原来标准错误输出(stderr)的内容了
[root /tmp ]$ ll t* fff sss 2>&1 | grep "fff"
ls: cannot access fff: No such file or directory

[root /tmp ]$ ll t* fff sss 2>&1 | grep "t.sh"
-rwxr--r-- 1 root root 291 Sep 28 18:00 t.sh

如果这时又要将输出信息导出到文件的话,我们能从很多博客或者教材中看到这样的写法:

[root /tmp ]$ ll t* fff sss 1>/tmp/aa 2>&1
[root /tmp ]$ cat aa
ls: cannot access fff: No such file or directory
ls: cannot access sss: No such file or directory
-rw-r--r-- 1 root root  29 Oct  8 15:11 t
-rwxr--r-- 1 root root 291 Sep 28 18:00 t.sh

然而,这样的话,标准输出(stdout)和错误输出(stderr)都到一起了,如果我们要将两个输出分开到两个文件存放(对!人生苦短,就是要这么钻牛角尖和丧心病狂)。

用这个来模拟问题就是:标准输出(stdout)导出到/tmp/aa文件,错误信息(stderr)中只留下”fff“文件的这条,其他的过滤

问题解决方法

这样其实也很容易,具体的方法就是:将”1>/tmp/aa“和”2>&1“位置对调下!

[root /tmp ]$ ll t* fff sss 2>&1 1>/tmp/aa | grep "fff"
ls: cannot access fff: No such file or directory
[root /tmp ]$ cat aa
-rw-r--r-- 1 root root  29 Oct  8 15:11 t
-rwxr--r-- 1 root root 291 Sep 28 18:00 t.sh
[root /tmp ]$

这样,正确信息导出到/tmp/aa文件中,错误信息中过滤了不想要的信息。

自己总结

当时刚看到对调下就能产生这么大的差别时,心里默念了句:”哎呀,我x,还能这么用,我当初数据流白学了“

自己琢磨应该是这么回事:

2>&1 1>/tmp/aa

bash处理输出时,先碰到了”2>&1“,于是把错误信息(stderr)输出到标准输出(stdout)的位置(此时是默认位置,屏幕)。之后碰到了”1>/tmp/aa“,于是把原来的标准输出(stdout,不考虑被导过来的错误信息)输出到指定的文件(此时已被指定到/tmp/aa文件)

1>/tmp/aa 2>&1

bash处理输出时,先碰到了”1>/tmp/aa“,于是把标准输出(stdout)输出到指定文件(/tmp/aa文件)。之后碰到了”2>&1“,于是把错误信息(stderr)输出到标准输出(stdout)的位置(此时已被指定到/tmp/aa文件)

以上的也只是我自己瞎捉摸来帮助自己理解的。真实原因我不清楚,有个疑问就是:”1>/tmp/aa 2>&1“输出到文件的顺序并没有那么一致的顺序。按照我琢磨的,应该是先标准输出(stdout)后错误信息(stderr)。实际文件中的顺序为先错误信息(stderr)后标准输出(stdout)。

one more thing

如果有人知道具体怎么解释,或者有管道这方面的资料的话,欢迎告诉我
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐