输出到stderr的回声

是否有一个标准的Bash工具,它的行为类似回声,但输出到stderr而不是stdout?

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

812447 次浏览

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

写个剧本

#!/bin/shecho $* 1>&2

这将是你的工具。

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

你可以定义一个函数:

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

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

Camilo Martin的bash具体建议使用“here字符串”并将打印您传递给它的任何内容,包括回显通常会吞下的参数(-n):

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

Glenn Jackman的解决方案也避免了论点吞咽问题:

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

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

#!/bin/bash# *************************************************************# This function redirect the pipe input to STDERR.## @param stream# @return string#function STDERR () {
cat - 1>&2
}
# remove the directory /bubuif rm /bubu 2>/dev/null; thenecho "Bubu is gone."elseecho "Has anyone seen Bubu?" | STDERRfi

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

由于1是标准输出,因此您不必在像>这样的输出重定向前显式命名它。相反,您可以简单地键入:

echo This message goes to stderr >&2

由于您似乎担心1>&2将难以可靠地输入,因此消除冗余的1可能会对您有所鼓励!

另一种选择

echo foo >>/dev/stderr

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

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

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

你可以这样做,这有助于阅读:

>&2 echo "error"

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

如果您不介意将消息也记录到syslog,not_so_ugly方法是:

logger -s $msg

-s选项表示:“将消息输出到标准错误以及系统日志。”

不要像这里提到的那样使用catcat是一个程序echoprintf是bash(shell)内置。启动程序或其他脚本(也在上面提到过)意味着不惜一切代价创建一个新进程。使用内置,编写函数非常便宜,因为不需要创建(执行)进程(-环境)。

开场白问“是否有任何标准工具可以将(管道)输出到stderr”,简短的回答是:没有…为什么?…重定向管道是unix(Linux…)和bash(sh)等系统中的基本概念。

我同意用这样的符号重定向的开场白:&2>1对现代程序员来说不是很愉快,但那是bash。Bash不是为了编写庞大而健壮的程序,它旨在帮助管理员用更少的按键完成工作;-)

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

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

注意:我正在回答后-而不是误导/模糊的“输出到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脚本更快
  • 可重用性-一个函数可以调用另一个函数
  • 可配置性-无需编辑原始脚本
  • 调试-更容易找到导致错误的行(特别是如果您正在处理大量重定向/过滤输出)
  • 健壮性-如果缺少函数并且您无法编辑脚本,您可以回退到使用同名的外部工具(例如log_error可以别名为Linux上的记录器)
  • 切换实现-您可以通过删除库的“x”属性切换到外部工具
  • 输出不可知-您不再需要关心它是否进入STDERR或其他地方
  • 个性化-您可以使用环境变量配置行为

我的建议:

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脚本中检测操作系统?可以提供帮助。

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

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

这仅使用Bash内置,同时使多行错误输出更不容易出错(因为您不必记住将&>2添加到每一行)。

詹姆斯·罗斯格伦·杰克曼建议的组合解决方案

  • 添加ANSI颜色代码以红色显示错误消息:
echoerr() { printf "\e[31;1m%s\e[0m\n" "$*" >&2; }
# if somehow \e is not working on your terminal, use \u001b instead# echoerr() { printf "\u001b[31;1m%s\u001b[0m\n" "$*" >&2; }
echoerr "This error message should be RED"

这是一个函数,用于检查最后一个命令的退出状态,显示错误并终止脚本。

or_exit() {local exit_status=$?local message=$*
if [ "$exit_status" -gt 0 ]thenecho "$(date '+%F %T') [$(basename "$0" .sh)] [ERROR] $message" >&2exit "$exit_status"fi}

用法:

gzip "$data_dir"or_exit "Cannot gzip $data_dir"
rm -rf "$junk"or_exit Cannot remove $junk folder

该函数打印出脚本名称和日期,以便在从crontab调用脚本并记录错误时有用。

59 23 * * * /my/backup.sh 2>> /my/error.log