1632

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).

2

15 Answers 15

2132

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.

10
  • 5
    I learn this trick quite a while ago. This page has some good information to it. tldp.org/LDP/abs/html/io-redirection.html Commented Oct 3, 2014 at 6:45
  • 66
    @BCS I dunno about using an alias in a shell script. It would probably be safer to use errcho(){ >&2 echo $@; } Commented Jul 13, 2015 at 21:52
  • 18
    >&2 is normally put at the end. This will work, but its used less often Commented Sep 8, 2015 at 23:59
  • 322
    In the nearly 40 years that I've been using Unix-like systems it has never occurred to me that you could put the redirect anywhere but at the end. Putting it up front like this makes it much more obvious (or "facilitates reading" as @MarcoAurelio says). +1 for teaching me something new.
    – Hephaestus
    Commented Nov 5, 2015 at 15:07
  • 41
    I always had troubles to understand how to read bash redirections but I think I've finally found the correct way to read them: It's important to note that 1 and 2 file descriptors are already pointing somewhere so when you write 2>&1 it means point 2 where 1 is _currently_ pointing to so now if you wrongly write 2>&1 >file, it reads point 2 where 1 is already going and point 1 to a file so 2 now goes where 1 used to go but 1 no longer goes there... On the contrary >file 2>&1 reads point 1 to this file and point 2 to where 1 is currently pointing to. & can thus be read as where.
    – 2072
    Commented Jan 24, 2021 at 17:41
543

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; }
12
  • 9
    I must say that echo is kinda unreliable. echoerr -ne xt is not going to print "-ne xt". Better use printf for that. Commented Jun 24, 2014 at 13:23
  • 13
    Oh, you can actually use cat too: echoerr() { cat <<< "$@" 1>&2; } Commented Jun 24, 2014 at 13:26
  • 9
    Or, printf "%s\n" "$*" >&2 Commented Mar 13, 2016 at 16:34
  • 2
    @GKFX no it doesn't, echoerr 'foo ​ bar' will have two spaces between foo and bar. (I had to add a zero-width joiner to make it show two spaces in this comment, so don't copy and paste it or it will be three spaces in the console). Commented Sep 29, 2016 at 12:49
  • 9
    @GKFX Of course it only works correctly when quoting. Why people don't quote their strings is beyond me. (when you don't quote, everything separated by one or more $IFS whitespace is sent as a separate argument, which in the case of echo means concatenating them with 0x20s, but the dangers of not quoting far outweight the convenience of 2 less characters to type). Commented Sep 29, 2016 at 19:56
464

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!

5
  • 37
    Just to note for myself - > &2 will not work, no whitespaces allowed there :) Commented May 7, 2021 at 20:05
  • 5
    I believe >& 2 should work though, if you want to add whitespace.
    – Bbrk24
    Commented Sep 11, 2022 at 20:25
  • isn't it better to use double symbol i.e. >>&2?
    – knocte
    Commented Aug 16, 2023 at 5:31
  • 3
    @knocte it makes no difference, as 2 is an open filehandle, not a path
    – OrangeDog
    Commented Oct 11, 2023 at 11:07
  • I use it this way >&2 echo "this goes out to stderr" I find it easier to look at.
    – Chris Reid
    Commented Jun 13 at 17:28
85

Another option

echo foo >>/dev/stderr
6
  • 8
    Is this option portable? Do someone know if this is not working for some unix flavour?
    – Dacav
    Commented Feb 17, 2014 at 16:26
  • 11
    It doesn't work in certain chroots, which can't access /dev/stderr. Commented Apr 28, 2014 at 22:35
  • 13
    If the script that executes this line - let's call it foo - has its own stderr redirected - e.g. foo >foo.log 2>&1 - then echo foo >/dev/stderr will clobber all the output before it. >> should be used instead: echo foo >>/dev/stderr
    – doshea
    Commented Sep 6, 2014 at 23:25
  • Similarly, you have /dev/fd/2.
    – jbruni
    Commented Apr 28, 2016 at 0:08
  • @Dacav this is for sure portable: /proc/self/fd/2. See my answer below :)
    – Sebastian
    Commented Sep 3, 2017 at 1:51
40

No, that's the standard way to do it. It shouldn't cause errors.

6
  • 10
    It shouldn't cause errors, but I might be more likely to. OTOH it's not that big a deal.
    – BCS
    Commented Jun 7, 2010 at 14:42
  • 6
    @Mike DeSimone: If someone else messes with the code, shuffles around the output, and doesn't actually know bash, they could easily drop (or mistype) the 1>&2. We all wish this wouldn't happen, but I'm sure we've all been places where it does.
    – Cascabel
    Commented Jun 7, 2010 at 15:00
  • 3
    ( echo something 1>&2 ; something else ) > log -> (echo something; cp some junk 1>&2 ; something else) > log Oops.
    – BCS
    Commented Jun 7, 2010 at 17:15
  • 35
    IMHO, if someone messes with the code and doesn't know bash, this may be the least of your problems. Commented Jun 7, 2010 at 17:34
  • 9
    I think if that's likely to be an issue, you should start using a different language: trying to make bash foolproof is a fool's venture.
    – intuited
    Commented Jun 7, 2010 at 23:19
33

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."

2
  • 4
    this is great! how portable is it?
    – code_monk
    Commented Jul 28, 2016 at 12:39
  • 3
    @code_monk: The logger command is expected to be IEEE Std 1003.2 ("POSIX.2") compatible, The logger command is part of the util-linux package and is available from Linux Kernel Archive ⟨kernel.org/pub/linux/utils/util-linux⟩.
    – miku
    Commented May 20, 2019 at 16:52
27

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).

3
  • 1
    Can't believe it, you vote me down when I recommend to use bash-redirect and in your own answer you are using bash-redirect.
    – return42
    Commented Feb 20, 2019 at 8:33
  • 3
    @return42 I voted your answer down because all it did was tell the OP that there's no better answer than what they started with.. it's not really an answer. I also don't see a sub-shell suggestion in your answer... your answer really just advises the OP not to use cat or any other utility, which is off-topic for the question.
    – GuyPaddock
    Commented Nov 27, 2019 at 19:09
  • what did I said: stackoverflow.com/questions/2990414/echo-that-outputs-to-stderr/…
    – return42
    Commented Nov 28, 2019 at 20:13
21

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:

  • nicest syntax possible (meaningful words instead of ugly symbols)
  • harder to make an error (especially if you reuse the script)
  • it's not a standard Bash tool, but it can be a standard shell library for you or your company/organization

Other reasons:

  • clarity - shows intention to other maintainers
  • speed - functions are faster than shell scripts
  • reusability - a function can call another function
  • configurability - no need to edit original script
  • debugging - easier to find the line responsible for an error (especially if you're deadling with a ton of redirecting/filtering output)
  • robustness - if a function is missing and you can't edit the script, you can fall back to using external tool with the same name (e.g. log_error can be aliased to logger on Linux)
  • switching implementations - you can switch to external tools by removing the "x" attribute of the library
  • output agnostic - you no longer have to care if it goes to STDERR or elsewhere
  • personalizing - you can configure behavior with environment variables
18

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.

3
  • 6
    The /proc/self link doesn't work on MacOS, so I'll stick with the more straight-forward /dev/stderr method. Also, as noted in other answers/comments, it is probably better to use >> to append.
    – MarkHu
    Commented Nov 22, 2017 at 0:30
  • 5
    /proc/self/fd/* is available on Termux on Android, but not /dev/stderr.
    – go2null
    Commented Feb 1, 2018 at 11:30
  • I used that to test on my current system it could be modified to test in different contexts to accomplish other tasks than echoing. Using this: echo -n "has '/proc/self/fd/2': " ; [ -e /proc/self/fd/2 ] && echo true || echo false or using that: echo -n "has '/dev/stderr': " ; [ -e /dev/stderr ] && echo true || echo false Commented Dec 5, 2020 at 12:02
16

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
5
  • 13
    Telling devs not to use programs only because of performance reason is premature optimization. Elegant, easy-to-follow approaches should preferred over hard-to-understand code that performs better (on the order of milliseconds).
    – GuyPaddock
    Commented Feb 18, 2019 at 0:01
  • 1
    @GuyPaddock sorry, you haven't read through this properly. Firs; Its about redirecting pipes which is well handled by the bash. If one do not like the (ugly) syntax how bash redirects, he should stop implementing bash scripts or learn the bash way. Second; you should know how expensive it is to launch a new prozess compared to yust call a bash builtin.
    – return42
    Commented Feb 18, 2019 at 12:26
  • 5
    There's a difference between letting someone know the performance trade-offs of Bash built-ins vs cat and instructing someone not to use cat because it's slow. There are countless use cases where cat is the right choice, so that's why I object to your answer.
    – GuyPaddock
    Commented Feb 18, 2019 at 21:56
  • @GuyPaddock The opener asked for a echo replacement. Even if he use cat, he has to use a bash redirect. anyway. So, there is absolute no sense in to use cat here. BTW I use cat 100 times a day, but never in the context the opener asked for... you got it?
    – return42
    Commented Feb 20, 2019 at 7:55
  • There are other shells that let you specify where to send your pipe. Not bash though.
    – Rolf
    Commented Sep 3, 2023 at 19:25
13

Combining solution suggested by James Roth and Glenn Jackman

  • add ANSI color code to display the error message in red:
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"
0
10

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
3
  • 3
    I think you can do the same thing with alias and be much more compact
    – BCS
    Commented Feb 3, 2012 at 15:54
  • or you can just pipe to the device file directly echo what | /dev/stderr ...
    – ardnew
    Commented Apr 3, 2020 at 1:30
  • Why use cat and incur the overhead of a new process for no gain? I prefer lolcat anyway.
    – sqqqrly
    Commented Aug 3, 2023 at 12:48
6

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.

1
  • 5
    Bash on OS X doesn't allow the "0.1"
    – James
    Commented Aug 29, 2013 at 19:49
5

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.

3
  • 6
    Better for it to be a function (like James Roth's answer), and better to pass along all arguments, not just the first.
    – Cascabel
    Commented Jun 7, 2010 at 14:59
  • 2
    Why would a function be better? (Or, alternatively: "Better to explain why it would be better...") Commented May 20, 2014 at 14:44
  • 5
    @OgrePsalm33 One reason a function would be better is that when calling a script, usually a new shell instance is created to provide an environment in which to execute the script. A function, on the other hand, is placed into the currently running shell's environment. Calling a function, in this case, would be a much more efficient operation since the creation of another instance of a shell would be avoided. Commented Dec 1, 2015 at 3:52
5

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.