How can I suppress output only if the command succeeds?

moreutils' chronic command does just that:

chronic mycommand

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


### do this bit once at the top of your script
divert=
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:

divert(){
    "$@" >&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