关于bash:echo输出到stderr

echo that outputs to stderr

是否有一个标准的bash工具像echo一样工作,但输出到stderr而不是stdout?

我知道我可以做echo foo 1>&2,但它有点难看,而且,我怀疑,容易出错(例如,当事情发生变化时,更容易被编辑错)。


这个问题很古老,但你可以这样做,这有助于阅读:

1
>&2 echo"error"

操作符>&2的字面意思是将文件描述符1(stdout的地址重定向到该命令1的文件描述符2(stderr的地址。根据您对它的理解程度,请阅读以下内容:http://wiki.bash-hacker.org/how to/redirection_tutorial

要避免与其他重定向交互,请使用子外壳

1
(>&2 echo"error")

1>&2将文件描述符2复制到文件描述符1。因此,在执行此重定向之后,两个文件描述符都将引用同一个文件:一个文件描述符2最初是引用到的。


您可以定义一个函数:

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

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

Camilo Martin的bash特定建议使用"here字符串",并将打印您传递给它的任何内容,包括echo通常会接受的参数(-n):

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

格伦·杰克曼的解决方案也避免了吞咽论点的问题:

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


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

1
echo This message goes to stderr >&2

由于您似乎担心1>&2对您来说很难可靠地输入,消除多余的1可能会对您有轻微的鼓励!


另一选择

1
echo foo >>/dev/stderr


不,这是标准的方法。它不应该导致错误。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/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


如果您不介意将消息也记录到syslog中,不那么难看的方法是:

1
logger -s $msg

-s选项意味着:"将消息输出到标准错误和系统日志。"


不要像这里提到的那样使用catcat是一个程序而echoprintf是bash(shell)内置的。启动一个程序或其他脚本(也如上所述)意味着用所有的成本创建一个新的过程。使用内置的,编写函数是相当便宜的,因为不需要创建(执行)一个进程(环境)。

opner询问"是否有任何标准工具输出(管道)到stderr",schort回答是:否…为什么?…在Unix(Linux…)和Bash(SH)等系统中,预测管道是建立在这些概念基础上的一个基本概念。

我同意开场白的观点,即用这样的符号重定向:&2>1对现代程序员来说不是很愉快,但这就是bash。bash不打算编写庞大而健壮的程序,它的目的是帮助管理员使用更少的按键进行工作;-)

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

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


注意:我回答的是帖子-不是误导性/模糊的"输出到stderr的回音"问题(已经被op回答)。

使用一个函数来显示意图和源代码您想要的实现。例如。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/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为:

1
2
3
4
5
6
7
8
9
10
11
12
13
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"; }

操作中处理问题的原因:

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

其他原因:

  • 清晰-向其他维护人员显示意图
  • 速度-函数比shell脚本快
  • 可重用性-一个函数可以调用另一个函数
  • 可配置性-无需编辑原始脚本
  • 调试-更容易找到导致错误的线路(特别是当你正在处理大量的重定向/过滤输出时)
  • 健壮性-如果函数丢失,并且无法编辑脚本,则可以使用具有相同名称的外部工具(例如,日志错误可以别名为Linux上的记录器)。
  • 切换实现-您可以通过删除库的"x"属性切换到外部工具。
  • 输出不可知-您不再需要关心它是否会进入stderr或其他地方。
  • 个性化-您可以使用环境变量配置行为

这个问题已经得到了答复,而且得到了很多选票。只是为了记录:

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

将有效输出到stderr。说明:/proc/self是当前进程的链接,/proc/self/fd保存进程打开的文件描述符。那么,012分别代表stdinstdoutstderr

我发现它更可读。而且,这可能适用于大多数Linux发行版:

echo"my errz"> /dev/stderr

使其更易于阅读。


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

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

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


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

1
2
3
4
5
    {
        echo"First error line"
        echo"Second error line"
        echo"Third error line"
    } >&2

这只使用了bash内置模块,同时减少了多行错误输出的错误倾向(因为您不必记住向每行添加&>2)。


制作剧本

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

那就是你的工具。

或者,如果不想在单独的文件中有一个脚本,就创建一个函数。


MacOSX:我尝试了被接受的答案和其他一些答案,所有这些都导致在我的Mac上写stdout而不是stderr。

下面是一种使用Perl编写标准错误的可移植方法:

1
echo WARNING! | perl -ne 'print STDERR'