I would like to simplify the output of a script by suppressing the output of secondary commands that are usually successful.

However, using -q on them hides the output when they occasionally fail, so I have no way of understanding the error. Additionally, these commands log their output on stderr.

Is there a way to suppress a command's output only if it succeeds?

For example (but not limited to) something like this:

mycommand | fingerscrossed

If all goes well, fingerscrossed catches the output and discards it. Else it echoes it to the standard or error output (whatever).


6 Answers 6


moreutils' chronic command does just that:

chronic mycommand

will swallow mycommand's output, unless it fails, in which case the output is displayed.

  • 2
    Thank you. I take it it's not installed by default on most Unix OSes? Jan 18, 2016 at 17:08
  • 1
    Probably not, although it's widely packaged so it should be easy to install. Jan 18, 2016 at 17:10
  • 1
    Debian has it in package moreutils. Nice for me, anyway :)
    – Tom Zych
    Jan 18, 2016 at 20:28
  • 7
    Note that it stores the whole output in memory. Jan 19, 2016 at 10:49
  • 1
    @StéphaneChazelas that's likely the only way to implement something like this, the output needs to be stored while the command is running in case it is needed.
    – Centimane
    Jan 19, 2016 at 13:52

I just found this much simpler answer on this other question:

output=`mycommand 2>&1` || echo $output

Works like a charm!

  • Note: for my use case this was a much simpler solution (avoid installing extra stuff on all CI servers) so I chose to mark this one as accepted. YMMV. Jan 22, 2016 at 13:35
  • Note that if you use set -o xtrace in your shell script then all the output will be there again as part of logging the details of the assignment output=... :-). In that case it's probably better to use chronic. Dec 5, 2019 at 19:40
  • One IMPORTANT difference from chronic -- if your script exits on failure (ala set -e) - chronic returns false, and execution will stop. If you want your simpler version to also stop, it needs: (echo $output && false)
    – Jeff Ward
    Sep 4, 2020 at 15:41
  • 2>&1 is the key!
    – Nishant
    Dec 10, 2020 at 11:05
  • This almost works perfectly, but it will suppress newlines from the result. (Or, at least, does for me.) Dec 15, 2023 at 13:22
### do this bit once at the top of your script
exec 3<>"${divert:=$(mktmp)}" 4<>/dev/null
rm -- "$divert"; unset divert
### then do this bit as often as needed
command >&3 2>&3
cat <&3 >&"$(((RTN=$?)?2:4))"

That should probably do the trick. It will buffer the output of each command into a deleted temporary file, and afterward siphon its output into either /dev/null or stderr depending on whether or not its return status was not zero. Because the temp file is deleted ahead of time it cannot be read by any process but the current shell and its children on its file descriptor (barring sneaky /proc/$pid/fd snoops with appropriate permissions), and it does not require cleaning up when you're through.

Perhaps a more convenient solution on linux systems:

    "$@" >&3 2>&3 ||
    eval "cat <&3
          return $?"
}   3<<"" 3<>/dev/fd/3

... which, in most shells, works much like the other, except that you can call it like: divert some simple-command with args. Beware of high output commands in "$@", though for dash, yash, or some other shells which do here-documents with pipes - I think it may be possible in those shells to fill the pipe buffer (at a default of around 128kb on linuxes) and so deadlock. That shouldn't be a worry for ksh, mksh, bash, zsh, or the Bourne shell, though - all of those do basically the same thing as I did explicitly above with exec.


Usually in case of error the command outputs messages to stderr so for you task you can just supress stdout

mycommand > /dev/null
  • 9
    Be careful Jan 18, 2016 at 22:38
  • Thank you, but as I said in the question my commands log all the output on stderr (so it has no effect). Jan 19, 2016 at 9:07

To make your own chronic

my_chronic() {
  tmp=$(mktemp) || return # this will be the temp file w/ the output
  "$@"  > "$tmp" 2>&1 # this should run the command, respecting all arguments
  [ "$ret" -eq 0 ] || cat "$tmp"  # if $? (the return of the last run command) is not zero, cat the temp file
  rm -f "$tmp"
  return "$ret" # return the exit status of the command

I do something like this in my makefiles:

if (mycommand) &> mycommand.log; then 
  echo success 
  echo;echo -e "Bad result from previous command, see mycommand.log for more details";echo;
  (exit $c)

Adapting that to your situation, you could do something like this:

if ! (mycommand) &> mycommand.log; then 
  cat mycommand.log
  rm mycommand.log
  (exit $c)

So, "if" runs the command and pipes the output to mycommand.log. If you need to catch stdout vs stdout vs whatever, you may need to change the pipe command '&>' to '>'. If the command fails then capture the error code, print out the contents of mycommand.log, remove mycommand.log, and finally return with the original error code.

Without the (exit $c) you would return with the exit code that matches the what the 'rm' command returned.

Finally, if you want a one liner, something like this would work.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log
  • 2
    Do you really wrap your commands/etc. in (...) like that? Because it doesn't do anything useful for you but spawn extra sub-shells. Jan 18, 2016 at 23:00
  • @EtanReisner (exit $c) is setting $?, which is something you can't do otherwise. if ! (mycommand) &>x is meaningful with the redirection if the command uses e.g. time or would give a shell error. Jan 19, 2016 at 20:30
  • @MichaelHomer - for those things there are { ; } the curlies... though exit is a little tricky there, admittedly.
    – mikeserv
    Jan 19, 2016 at 21:14
  • In a makefile snippet if you are trying to exit with the previously saved $? then you can just use exit $c but yes, in other cases (exit $?) has value (though a shell function rret() { return $1; } would be better in general I'd argue). The subshell for commands still doesn't really though as mikeserv indicated. Jan 20, 2016 at 2:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .