输出到stderr的回声


1113

是否有一个标准的Bash工具,其作用类似于echo,但输出到stderr而不是stdout?

我知道我可以做,echo foo 1>&2但是有点丑陋,而且我怀疑容易出错(例如,当事情改变时,更容易被错误地编辑)。


Answers:


1450

您可以执行此操作,以方便阅读:

>&2 echo "error"

>&2将文件描述符#2复制到文件描述符#1。因此,执行此重定向后,两个文件描述符都将引用同一文件:最初引用了一个文件描述符#2 。有关更多信息,请参见《Bash黑客图解重定向教程》


2
我很早以前就学会了这个技巧。此页面上有一些很好的信息。tldp.org/LDP/abs/html/io-redirection.html
Marco Aurelio

46
@BCS我不知道alias在shell脚本中使用。或许,这将是更安全的使用errcho(){ >&2 echo $@; }
布雷登最佳

3
>&2通常放在末尾。这将起作用,但使用频率较低
Iskren Ivov Chernev

158
在我使用类似Unix的系统的近40年中,我从来没有想到可以将重定向放在任何地方,但要放在最后。像这样将其放在最前面会使它更加明显(或如@MarcoAurelio所说的“便于阅读”)。+1教我一些新知识。
Hephaestus

仅供参考:如果您要设置格式或执行其他操作,而不仅仅是回显字符串,则必须将重定向移回末尾。例如,errcho(){ >&2 echo $@|pr -To5;}将无法正常工作。要执行类似的操作,您必须将重定向放置在最后一个管道之后,例如:errcho(){ echo $@|>&2 pr -To5;}
Jon Red

423

您可以定义一个函数:

echoerr() { echo "$@" 1>&2; }
echoerr hello world

这将比脚本快,并且没有依赖性。

卡米洛·马丁(Camilo Martin)的bash特定建议使用“此处字符串”,并将打印您传递给它的任何内容,包括回显通常会吞咽的参数(-n):

echoerr() { cat <<< "$@" 1>&2; }

Glenn Jackman的解决方案还避免了参数吞咽问题:

echoerr() { printf "%s\n" "$*" >&2; }

7
我必须说,回声有点不可靠。echoerr -ne xt不会打印“ -ne xt”。更好地使用printf它。
卡米洛·马丁

10
哦,您实际上也可以使用猫:echoerr() { cat <<< "$@" 1>&2; }
Camilo Martin

2
我没有意识到。添加。
James Roth 2014年

4
或者,printf "%s\n" "$*" >&2
格伦·杰克曼

4
@GKFX当然,只有在引用时它才能正确运行。人们为什么不引用他们的字符串,这超出了我的范围。(当您不引用时,由一个或多个$IFS空格分隔的所有内容将作为一个单独的参数发送,在这种情况下,echo意味着将它们与0x20s 相连,但是如果不加引号,则很可能会浪费太少的两个字符) 。
卡米洛·马丁

252

由于1是标准输出,因此您不必在输出重定向之前显式命名它,>而只需键入:

echo此消息转到stderr>&2

由于您似乎担心1>&2很难进行可靠的键入,因此消除冗余1可能会给您带来一点鼓励!


59

另外一个选项

echo foo >>/dev/stderr

4
此选项可移植吗?有人知道这对某些unix口味是否无效?
达卡夫2014年

7
在某些不能访问/ dev / stderr的chroot中,它不起作用。
Zachary Vance 2014年

12
如果执行此行的脚本(我们称其为脚本)foo具有自己的stderr重定向(例如foo >foo.log 2>&1-),则echo foo >/dev/stderr它将覆盖之前的所有输出。 >>应该改为使用:echo foo >>/dev/stderr
doshea 2014年

同样,您有/dev/fd/2
jbruni

@Dacav肯定是可移植的:/proc/self/fd/2。请参阅下面的回答:)
塞巴斯蒂安(Sebastian)

31

不,这是标准的方法。它不应该引起错误。


9
不应该引起错误,但是我可能会出错。太太没什么大不了的。
BCS 2010年

6
@Mike DeSimone:如果其他人弄乱了代码,在输出周围随机播放,并且实际上并不了解bash,则他们很容易丢弃(或键入错误)1>&2。我们都希望不会发生这种情况,但是我敢肯定,我们到过的地方都有。
卡斯卡贝尔

2
( echo something 1>&2 ; something else ) > log-> (echo something; cp some junk 1>&2 ; something else) > log糟糕。
BCS 2010年

29
恕我直言,如果有人弄乱了代码并且不懂bash,那么这可能是最少的问题。
Mike DeSimone 2010年

7
我认为如果这可能是一个问题,则应该开始使用另一种语言:尝试使bash变得万无一失是一种愚蠢的冒险。
直觉

17

如果您不介意将消息也记录到syslog中,则not_so_ugly的方式是:

logger -s $msg

-s选项的意思是:“将消息输出到标准错误以及系统日志中。”


1
这很棒!它有多便携?
code_monk '16

@code_monk:记录器命令预期为IEEE 1003.2(“POSIX.2”)兼容,记录器命令是使用util-linux软件包的一部分,并且可从Linux内核存档⟨ kernel.org/pub/linux/utils / util- linux⟩。
miku

12

这是一个简单的STDERR函数,它将管道输入重定向到STDERR。

#!/bin/bash
# *************************************************************
# This function redirect the pipe input to STDERR.
#
# @param stream
# @return string
#
function STDERR () {

cat - 1>&2

}

# remove the directory /bubu
if rm /bubu 2>/dev/null; then
    echo "Bubu is gone."
else
    echo "Has anyone seen Bubu?" | STDERR
fi


# run the bubu.sh and redirect you output
tux@earth:~$ ./bubu.sh >/tmp/bubu.log 2>/tmp/bubu.err

2
我认为您可以使用别名做同样的事情,并且要紧凑得多
BCS 2012年

或者,您也可以直接通过管道将其传输到设备文件echo what | /dev/stderr...
ardnew

11

注意:我是在回答帖子,而不是误导性/模糊的“输出到stderr的回声”问题(已由OP回答)。

使用函数显示意图并提供所需的实现。例如

#!/bin/bash

[ -x error_handling ] && . error_handling

filename="foobar.txt"
config_error $filename "invalid value!"

output_xml_error "No such account"

debug_output "Skipping cache"

log_error "Timeout downloading archive"

notify_admin "Out of disk space!"

fatal "failed to open logger!"

并且error_handling

ADMIN_EMAIL=root@localhost

config_error() { filename="$1"; shift; echo "Config error in $filename: $*" 2>&1; }

output_xml_error() { echo "<error>$*</error>" 2>&1; }

debug_output() { [ "$DEBUG"=="1" ] && echo "DEBUG: $*"; }

log_error() { logger -s "$*"; }

fatal() { which logger >/dev/null && logger -s "FATAL: $*" || echo "FATAL: $*"; exit 100; }

notify_admin() { echo "$*" | mail -s "Error from script" "$ADMIN_EMAIL"; }

处理OP中问题的原因:

  • 最好的语法(有意义的单词而不是难看的符号)
  • 很难出错(尤其是如果您重复使用脚本)
  • 它不是标准的Bash工具,但可以是您或您的公司/组织的标准Shell库

其他原因:

  • 清晰-向其他维护者表明意图
  • 速度-函数比shell脚本快
  • 可重用性-一个函数可以调用另一个函数
  • 可配置性-无需编辑原始脚本
  • 调试-更容易找到导致错误的行(特别是如果您因大量重定向/过滤输出而陷入困境)
  • 健壮性-如果缺少某个函数并且您无法编辑脚本,则可以退回使用具有相同名称的外部工具(例如,在Linux上,可以将log_error别名为logger)
  • 切换实现-您可以通过删除库的“ x”属性来切换到外部工具
  • 输出不可知-您不再需要关心它是否进入STDERR或其他地方
  • 个性化-您可以使用环境变量配置行为

10

我的建议:

echo "my errz" >> /proc/self/fd/2

要么

echo "my errz" >> /dev/stderr

echo "my errz" > /proc/self/fd/2将有效输出stderr,因为/proc/self是当前进程的链接,并/proc/self/fd持有进程打开的文件描述符,然后012代表stdinstdoutstderr分别。

/proc/self链接在MacOS /proc/self/fd/*上不起作用,但是可在Android上的Termux上使用,但不能使用/dev/stderr如何从Bash脚本检测操作系统?如果需要通过使用哪种变体来使脚本更易于移植,可以提供帮助。


5
/proc/self链接在MacOS上不起作用,因此我会坚持使用更简单的/dev/stderr方法。另外,如其他答案/评论中所述,最好使用>>追加。
MarkHu '17

4
/proc/self/fd/*在Android上的Termux上可用,但不可用/dev/stderr
go2null18年

9

不要使用,cat因为这里提到了一些。cat是 while 的程序echoprintf是bash(shell)内置函数。启动程序或其他脚本(也在上面提到)意味着要花所有的成本创建一个新的过程。使用内置函数,编写函数非常便宜,因为不需要创建(执行)进程(-环境)。

操作者问“是否有标准工具可将(管道)输出到stderr”,schort的回答是:NO ...为什么?...重新分配管道是unix(Linux ...)等系统中的一个基本概念,而bash(sh)建立在这些概念上。

我同意开场白,即用这样的符号进行重定向:&2>1对于现代程序员来说不是很令人愉快,但这是个b脚。Bash并不是要编写庞大而健壮的程序,而是要帮助管理员以更少的按键来实现工作;-)

至少,您可以将重定向放置在行中的任何位置:

$ echo This message >&2 goes to stderr 
This message goes to stderr

1
告诉开发人员不要仅因为性能原因而使用程序是过早的优化。优雅,易于执行的方法应优先于难以理解的,性能更好的代码(以毫秒为单位)。
GuyPaddock

@GuyPaddock抱歉,您没有正确阅读此内容。冷杉;它是关于由bash很好处理的重定向管道的。如果不喜欢bash重定向的(丑陋的)语法,他应该停止实现bash脚本或学习bash方式。第二; 您应该知道,发动新的Pross相比调用内置的Bash昂贵。
return42

1
让某人知道Bash内建组件的性能折衷cat与指示某人不要使用cat(因为它很慢)之间有区别。在无数用例中,cat是正确的选择,所以这就是为什么我反对您的答案。
GuyPaddock

@GuyPaddock开瓶器要求echo更换。即使使用cat,他也必须使用bash重定向。无论如何。因此,这里绝对没有意义cat。顺便说一句,我cat每天使用100次,但是在这种情况下,开瓶器从来没有要求...您知道吗?
return42

8

我最近偶然发现的另一个选择是:

    {
        echo "First error line"
        echo "Second error line"
        echo "Third error line"
    } >&2

这仅使用Bash内置函数,同时使多行错误输出的错误发生率降低(因为您不必记住要添加&>2到每一行)。


1
真不敢相信,当我建议使用bash-redirect时,您将我否决,而在您自己的答案中,您正在使用bash-redirect。
return42

1
@ return42我否决了您的答案,因为它所做的只是告诉OP,没有比他们开始时更好的答案了。.这并不是真正的答案。我也没有在您的答案中看到子外壳建议...您的答案实际上只是建议OP不要使用cat或使用任何其他实用程序,这对这个问题来说是不合时宜的。
GuyPaddock


6

read 是shell内置命令,可打印到stderr,可以像echo一样使用,而无需执行重定向技巧:

read -t 0.1 -p "This will be sent to stderr"

-t 0.1是一个超时,它禁用了read的主要功能,将一行stdin存储到一个变量中。


5
Bash在OS X上不允许使用“ 0.1”
James Roth

2

制作脚本

#!/bin/sh
echo $* 1>&2

那将是您的工具。

或者,如果您不想在单独的文件中包含脚本,请创建函数。


6
最好是将其作为一个函数(如James Roth的回答),并更好地传递所有参数,而不仅仅是第一个参数。
卡斯卡贝尔

2
为什么功能会更好?(或者,或者:“更好地解释为什么会更好……”)
Ogre Psalm33,2014年

3
@ OgrePsalm33函数会更好的一个原因是,在调用脚本时,通常会创建一个新的Shell实例以提供执行脚本的环境。另一方面,一个函数被放置在当前正在运行的shell环境中。在这种情况下,调用函数将是效率更高的操作,因为可以避免创建外壳的另一个实例。
destenson

-10

Mac OS X:我尝试了接受的答案以及其他几个答案,所有这些导致我在Mac上编写了STDOUT而不是STDERR。

这是使用Perl写入标准错误的可移植方法:

echo WARNING! | perl -ne 'print STDERR'

大声笑下来所有你想要的,但这是我在我的代码中实际使用的解决方案!
诺亚·萨斯曼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.