Reputation: 78506
Is there a standard Bash command that acts like echo
but outputs to stderr rather than stdout?
I know I can do echo foo 1>&2
but it's kinda ugly and, I suspect, error-prone (e.g. more likely to get edited wrong when things change).
Upvotes: 1660
Views: 1007132
Reputation: 22126
You could do this, which facilitates reading:
>&2 echo "error"
>&2
copies file descriptor #2 to file descriptor #1. Therefore, after this redirection is performed, both file descriptors will refer to the same file: the one file descriptor #2 was originally referring to. For more information see the Bash Hackers Illustrated Redirection Tutorial.
Upvotes: 2170
Reputation: 89325
Since 1
is the standard output, you do not have to explicitly name it in front of an output redirection like >
. Instead, you can simply type:
echo This message goes to stderr >&2
Since you seem to be worried that 1>&2
will be difficult for you to reliably type, the elimination of the redundant 1
might be a slight encouragement to you!
Upvotes: 475
Reputation: 561
Don't use cat
as some have mentioned here. cat
is a program
while echo
and printf
are bash (shell) builtins. Launching a program or another script (also mentioned above) means to create a new process with all its costs. Using builtins, writing functions is quite cheap, because there is no need to create (execute) a process (-environment).
The opener asks "is there any standard tool to output (pipe) to stderr", the short answer is : NO ... why? ... redirecting pipes is an elementary concept in systems like unix (Linux...) and bash (sh) builds up on these concepts.
I agree with the opener that redirecting with notations like this: &2>1
is not very pleasant for modern programmers, but that's bash. Bash was not intended to write huge and robust programs, it is intended to help the admins to get there work with less keypresses ;-)
And at least, you can place the redirection anywhere in the line:
$ echo This message >&2 goes to stderr
This message goes to stderr
Upvotes: 16
Reputation: 3383
Here is a function for checking the exit status of the last command, showing error and terminate the script.
or_exit() {
local exit_status=$?
local message=$*
if [ "$exit_status" -gt 0 ]
then
echo "$(date '+%F %T') [$(basename "$0" .sh)] [ERROR] $message" >&2
exit "$exit_status"
fi
}
Usage:
gzip "$data_dir"
or_exit "Cannot gzip $data_dir"
rm -rf "$junk"
or_exit Cannot remove $junk folder
The function prints out the script name and the date in order to be useful when the script is called from crontab
and logs the errors.
59 23 * * * /my/backup.sh 2>> /my/error.log
Upvotes: 5
Reputation: 6781
Combining solution suggested by James Roth and Glenn Jackman
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"
Upvotes: 13
Reputation: 5835
My suggestion:
echo "my errz" >> /proc/self/fd/2
or
echo "my errz" >> /dev/stderr
echo "my errz" > /proc/self/fd/2
will effectively output to stderr
because /proc/self
is a link to the current process, and /proc/self/fd
holds the process opened file descriptors, and then, 0
, 1
, and 2
stand for stdin
, stdout
and stderr
respectively.
The /proc/self
link doesn't work on MacOS, however, /proc/self/fd/*
is available on Termux on Android, but not /dev/stderr
. How to detect the OS from a Bash script? can help if you need to make your script more portable by determining which variant to use.
Upvotes: 18
Reputation: 2517
Another option that I recently stumbled on is this:
{
echo "First error line"
echo "Second error line"
echo "Third error line"
} >&2
This uses only Bash built-ins while making multi-line error output less error prone (since you don't have to remember to add &>2
to every line).
Upvotes: 27
Reputation: 2095
Note: I'm answering the post- not the misleading/vague "echo that outputs to stderr" question (already answered by OP).
Use a function to show the intention and source the implementation you want. E.g.
#!/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!"
And error_handling
being:
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"; }
Reasons that handle concerns in OP:
Other reasons:
Upvotes: 21
Reputation: 7198
You could define a function:
echoerr() { echo "$@" 1>&2; }
echoerr hello world
This would be faster than a script and have no dependencies.
Camilo Martin's bash specific suggestion uses a "here string" and will print anything you pass to it, including arguments (-n) that echo would normally swallow:
echoerr() { cat <<< "$@" 1>&2; }
Glenn Jackman's solution also avoids the argument swallowing problem:
echoerr() { printf "%s\n" "$*" >&2; }
Upvotes: 548
Reputation: 10492
If you don't mind logging the message also to syslog, the not_so_ugly way is:
logger -s $msg
The -s option means: "Output the message to standard error as well as to the system log."
Upvotes: 34
Reputation: 21747
read
is a shell builtin command that prints to stderr, and can be used like echo without performing redirection tricks:
read -t 0.1 -p "This will be sent to stderr"
The -t 0.1
is a timeout that disables read's main functionality, storing one line of stdin into a variable.
Upvotes: 6
Reputation: 161
This is a simple STDERR function, which redirect the pipe input to 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
Upvotes: 10
Reputation: 12610
Make a script
#!/bin/sh
echo $* 1>&2
that would be your tool.
Or make a function if you don't want to have a script in separate file.
Upvotes: 5
Reputation: 284786
No, that's the standard way to do it. It shouldn't cause errors.
Upvotes: 40