我有一个要写入信息的程序stdout
和stderr
,我需要grep
通过报应的标准错误,而忽视标准输出。
我当然可以分两步完成:
command > /dev/null 2> temp.file
grep 'something' temp.file
但我希望能够在没有临时文件的情况下执行此操作。有任何智能管道技巧吗?
command 2| othercommand
。Bash非常完美,以致于1982年开发结束,所以恐怕我们再也不会看到Bash了。
我有一个要写入信息的程序stdout
和stderr
,我需要grep
通过报应的标准错误,而忽视标准输出。
我当然可以分两步完成:
command > /dev/null 2> temp.file
grep 'something' temp.file
但我希望能够在没有临时文件的情况下执行此操作。有任何智能管道技巧吗?
command 2| othercommand
。Bash非常完美,以致于1982年开发结束,所以恐怕我们再也不会看到Bash了。
Answers:
首先将stderr重定向到stdout-管道;然后将stdout重定向到/dev/null
(不更改stderr的去向):
command 2>&1 >/dev/null | grep 'something'
有关各种I / O重定向的详细信息,请参见Bash参考手册中有关重定向的章节。
请注意,I / O重定向的顺序是从左到右解释的,但是在解释I / O重定向之前已设置了管道。文件描述符(例如1和2)是对打开文件描述的引用。该操作2>&1
使文件描述符2 aka stderr引用与当前所引用的文件描述符1 aka stdout相同的打开文件描述(请参阅dup2()
和open()
)。>/dev/null
然后,该操作会更改文件描述符1,使其引用的打开文件描述/dev/null
,但这不会改变文件描述符2指向文件描述符1最初指向的打开文件描述(即管道)这一事实。
command 2> /dev/stdout 1> /dev/null | grep 'something'
/dev/stdout
et al或使用/dev/fd/N
。除非外壳将它们视为特殊情况,否则它们的效率将略有降低;纯数字符号不涉及按名称访问文件,但是使用设备确实意味着对文件名的查找。您是否可以衡量这一点值得商bat。我喜欢数字符号的简洁性-但是我使用它已经很长时间了(超过25个世纪;哎呀!),以至于我没有资格判断它在现代世界中的优点。
2>&1
,这意味着“将stderr连接到stdout 当前要去的文件描述符”。第二个操作是“更改stdout,使其转到/dev/null
”,将stderr转到原始stdout,即管道。Shell首先在管道符号处分割内容,因此管道重定向发生在2>&1
或>/dev/null
重定向之前,仅此而已;其他操作从左到右。(从右到左不起作用。)
/dev/null
为Windows 后nul
)。
或交换标准错误和标准输出的输出,请使用:
command 3>&1 1>&2 2>&3
这将创建一个新的文件描述符(3),并将其分配到与1(标准输出)相同的位置,然后将fd 1(标准输出)分配给与fd 2(标准错误)相同的位置,最后分配fd 2(标准错误) )与fd 3(标准输出)的位置相同。
现在可以使用标准误差作为标准输出,并且将旧的标准输出保留在标准误差中。这可能有些大材小用,但希望能提供有关Bash文件描述符的更多详细信息(每个进程有9个可用)。
3>&-
关闭您从stdout创建的备用描述符
stderr
具有的结合,另一个stderr
与stdout
?换句话说,可以一次stderr
转到两个不同的文件吗?
在Bash中,您还可以使用流程替换重定向到子shell :
command > >(stdlog pipe) 2> >(stderr pipe)
对于当前情况:
command 2> >(grep 'something') >/dev/null
command 2> >(grep 'something' > grep.log)
grep.log包含来自输出作为ungrepped.log同一样command 2> ungrepped.log
2> >(stderr pipe >&2)
。否则,“ stderr管道”的输出将通过“ stdlog管道”。
2> >(...)
有效,我尝试过2>&1 > >(...)
但没有尝试
awk -f /new_lines.awk <in-content.txt > out-content.txt 2> >(tee new_lines.log 1>&2 )
在这种情况下,我想还看看有什么出来作为我的控制台上的错误。但是STDOUT将转到输出文件。因此,在子外壳内,您需要将该STDOUT重定向回括号内的STDERR。在此期间,该tee
命令的STDOUT输出将在out-content.txt
文件末尾结束。这对我来说似乎很矛盾。
2>&1 1> >(dest pipe)
如果您这样做,请结合以下最佳答案:
command 2> >(grep -v something 1>&2)
...然后所有stdout都保留为stdout ,所有stderr都保留为stderr,但是您不会在stderr中看到任何包含字符串“ something”的行。
这具有不反转或丢弃stdout和stderr,不将它们混在一起或使用任何临时文件的独特优势。
command 2> >(grep -v something)
(不1>&2
)一样吗?
tar cfz my.tar.gz mydirectory/ 2> >(grep -v 'changed as we read it' 1>&2)
应该工作。
如果您考虑“重定向”和“管道”的实际情况,则将事情可视化会容易得多。bash中的重定向和管道可以做一件事:修改进程文件描述符0、1和2指向的位置(请参阅/ proc / [pid] / fd / *)。
当管道或“ |” 如果操作员出现在命令行中,则第一件事就是bash创建了一个fifo,并将左侧命令的FD 1指向该fifo,并将右侧命令的FD 0指向了同一fifo。
接下来,从左到右评估每一侧的重定向运算符,并在描述符重复出现时使用当前设置。这一点很重要,因为自从首先建立管道以来,FD1(左侧)和FD0(右侧)已经从它们通常的状态中进行了更改,并且任何重复都将反映这一事实。
因此,当您键入类似以下内容:
command 2>&1 >/dev/null | grep 'something'
这是按顺序发生的事情:
因此,“命令”写入其FD 2(stderr)的所有输出均进入管道,并由另一侧的“ grep”读取。“命令”写入其FD 1(stdout)的所有输出均进入/ dev / null。
如果相反,则运行以下命令:
command >/dev/null 2>&1 | grep 'something'
这是发生了什么:
因此,“命令”中的所有stdout和stderr都转到/ dev / null。没有任何东西进入管道,因此“ grep”将关闭而不在屏幕上显示任何内容。
另请注意,重定向(文件描述符)可以是只读(<),只写(>)或读写(<>)。
最后的笔记。程序是向FD1还是FD2写东西,完全取决于程序员。良好的编程习惯规定,错误消息应该发送到FD 2,正常输出应该发送到FD 1,但是您经常会发现混合这两者的草率编程,或者忽略了约定。
如果您使用的是Bash,请使用:
command >/dev/null |& grep "something"
http://www.gnu.org/software/bash/manual/bashref.html#Pipelines
|&
等于2>&1
将stdout和stderr组合在一起。该问题明确要求没有标准输出的输出。
>/dev/null |&
扩展至>/dev/null 2>&1 |
,这意味着stdout inode是空的,因为没有人(#1#2都绑定到/ dev / null inode)绑定到stdout inode(例如,ls -R /tmp/* >/dev/null 2>&1 | grep i
将为空,但ls -R /tmp/* 2>&1 >/dev/null | grep i
将让#与stdout inode绑定的2将通过管道传输)。
( echo out; echo err >&2 ) >/dev/null |& grep "."
但未提供任何输出(我们需要“错误”的位置)。man bash
说如果使用了&&…是2>&1 |的简写。将标准错误隐式重定向到标准输出是在命令指定的任何重定向之后执行的。因此,首先我们将命令的FD1重定向到null,然后将命令的FD2重定向到FD1指向的位置。null,因此grep的FD0没有输入。有关更深入的说明,请参见stackoverflow.com/a/18342079/69663。
对于那些想要将stdout和stderr永久重定向到文件的人,请在stderr上使用grep,但保留stdout以便将消息写入tty:
# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3
我尝试关注,发现它也能正常工作,
command > /dev/null 2>&1 | grep 'something'