11

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
  • 2
    If you redirect only stdout, stderr will still show up; is this sufficient for you, or do you want to see stdout as well if there's an error? – Kromey Jun 25 '14 at 16:19
  • I want to see both but only if something goes wrong, otherwise I don't want to see anything. – Grzegorz Adam Hankiewicz Jun 25 '14 at 16:32
  • 2
    What I do is print stdout & stderr to a logfile so it doesn't clutter the screen. I also print stderr to the screen, so if there is and error I can see it. If I need details I can check the logfile, which contains the program output and the program errors in context. This way, I can 'see both but only if something goes wrong'. Does this help? See stackoverflow.com/questions/2871233/… – Stefan Lasiewski Jun 25 '14 at 17:26
  • Is it safe to redirect stderr and stdout to the same file with two separate redirects? I thought we should always use 2>&1, something like: $* >>"${SILENT_LOG}" 2>&1" || report_and_exit – psmith Feb 13 '18 at 11:23
3

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
  • An attacker who has non-root access on your system, could try to make a symlink between rm and command call, which would point to /etc/passswd or some other critical file and get the contents overwritten. – Mitar Jun 18 '17 at 18:55
  • 1
    BTW, order of redirects above should be: $* > /tmp/surpress.out 2>&1 This really captures the stderr. – Mitar Jun 19 '17 at 17:32
  • 2
    $* is not the best to handle arbitrary input. Especially when it contains spaces or flags. Most portable is ${1+"$@"} according to stackoverflow.com/questions/743454/… – balrok Oct 17 '17 at 13:38
  • Modified per both comments. Thanks---good info. – Belmin Fernandez Oct 18 '17 at 14:37
  • Spelling merriam-webster.com/dictionary/suppress – Hatshepsut Mar 5 '18 at 19:45
6

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 $?
5

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.

  • Should be doable with a function. Hm, let me give it a try. – Belmin Fernandez Jun 25 '14 at 17:43
1

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

1

Try so:

out=`command args...` || echo $out
  • 1
    I'd write it as out="$(command args...)" || echo "$out" – kasperd Jun 26 '14 at 13:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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