Hide the output of a shell command only on success?
Hiding the output of a shell command usually involves redirecting stderr and stdout. Is there any builtin facility or command which by default hides the output but on error dumps all the accumulated output? I would like to run this as a wrapper for remote ssh
commands. Now I have them using redirection but I don't get a clue as to what made them fail, and they are just too verbose.
EDIT: In the end I created the following template based on the answer by @Belmin which I tweaked a little bit to accumulate all the previous commands from the script, use the current process identifier, automatically remove the log, and add a failure red error message when something goes wrong. In this template the initial silent
wrappers will succeed, then fail the third command because the directory already exists:
#!/bin/sh
set -e
SILENT_LOG=/tmp/silent_log_$$.txt
trap "/bin/rm -f $SILENT_LOG" EXIT
function report_and_exit {
cat "${SILENT_LOG}";
echo "\033[91mError running command.\033[39m"
exit 1;
}
function silent {
$* 2>>"${SILENT_LOG}" >> "${SILENT_LOG}" || report_and_exit;
}
silent mkdir -v pepe
silent mkdir -v pepe2
silent mkdir -v pepe
silent mkdir -v pepe2
I'd setup a bash function like this:
function suppress
{
/bin/rm --force /tmp/suppress.out 2> /dev/null; \
${1+"$@"} > /tmp/suppress.out 2>&1 || \
cat /tmp/suppress.out; \
bin/rm /tmp/suppress.out;
}
Then, you could just run the command:
suppress foo -a bar
It should be easy enough to write a script for this purpose.
Something like this completely untested script.
OUTPUT=`tempfile`
program_we_want_to_capture &2>1 > $OUTPUT
[ $? -ne 0 ]; then
cat $OUTPUT
exit 1
fi
rm $OUTPUT
On the other hand for commands I run as part of a script I usually want something better than simply print all the output. I often limit what I see to the unknown. Here is a script I adapted from something I read over a decade ago.
#!/bin/bash
the_command 2>&1 | awk '
BEGIN \
{
# Initialize our error-detection flag.
ErrorDetected = 0
}
# Following are regex that will simply skip all lines
# which are good and we never want to see
/ Added UserList source/ || \
/ Added User/ || \
/ init domainlist / || \
/ init iplist / || \
/ init urllist / || \
/ loading dbfile / || \
/^$/ {next} # Uninteresting message. Skip it.
# Following are lines that we good and we always want to see
/ INFO: ready for requests / \
{
print " " $0 # Expected message we want to see.
next
}
# any remaining lines are unexpected, and probably error messages. These will be printed out and highlighted.
{
print "->" $0 # Unexpected message. Print it
ErrorDetected=1
}
END \
{
if (ErrorDetected == 1) {
print "Unexpected messages (\"->\") detected in execution."
exit 2
}
}
'
exit $?
I don't think there is a clean way of doing this, the only thing I can think of is
- Capture the output of the command.
- Check the return value of the command and if it failed
- display the captured output.
Implementing this might though be a interesting project but perhaps beyond Q&A.
going short with something like tehcommand &>/tmp/$$ || cat /tmp/$$
depends how much usability/typing you want/need. (e.g. using it as a pipe or passing the command by argument)
@zoredache short script is basically a proto-wrapper for this, which would give more robustness, handle concurrency, etc
Try so:
out=`command args...` || echo $out