5

I have a couple of scripts in my bash file, that should do certain tasks, say like ...

#!bin/bash
yum -y update
service restart nginx
yum install atop
cat /var/log/somelog.log > /home/cron/php/script

It goes on and on, but the problem is that with each task taken, bash shows me the output. Like service restart nginx for example, outputs some message. And I want to hide all these messages. What is generally the accepted way of achieving this? Because, I was about to redirect the STDOUT to /dev/null but, considering I have over 50 consecutive tasks to run, it would mean I have to /dev/null that much, which for some reason does not seem efficient to me.

9

Redirect all the output as a block.

(
   yum -y update

   service restart nginx

   yum install atop

   cat /var/log/somelog.log > /home/cron/php/script

) > /dev/null 2>&1
  • This seems like it, but it wouldn't allow me to have arbitrary messages, for like yum update and see, if it failed, to echo something. Because, your script would ignore everything – robue-a7119895 Oct 31 '14 at 14:15
  • @Contax there is no difference between having this in your script or running script.sh >/dev/null 2>&1. Redirecting to /dev/null is the way to silence output. – terdon Oct 31 '14 at 14:44
  • 1
    If you want to see errors, and not see standard output, just leave off the 2>&1 at the end. – Omnipresence Oct 31 '14 at 15:00
8

You can save the file descriptors of stdout and stderr, overwrite them, and restore them after the programs have been run:

exec 3>&1
exec 4>&2
exec 1>/dev/null
exec 2>/dev/null
./script-1
./script-2
...
./script-n
exec 1>&3
exec 2>&4
# delete the copies
exec 3>&-
exec 4>&-
  • Thanks, I didn't know this was possible. A bit over my grasp for the moment, but thanks – robue-a7119895 Oct 31 '14 at 14:23
2

If you want to hide all output from the commands you're running (both output and errors), but still be able to print messages yourself, then Hauke Laging's approach is absolutely the right way to do this.

It lets you keep a reference to stdout (aka file descriptor 1) and stderr (aka file descriptor 2), redirect them to /dev/null, but still use them if you want a message to be shown. I'd just like to add a bunch of commenting that explains exactly what it's doing.

exec 3>&1 # Open file descriptor 3, writing to wherever stdout currently writes
exec 4>&2 # Open file descriptor 4, writing to wherever stderr currently writes

exec 1>/dev/null # Redirect stdout to /dev/null, leaving fd 3 alone
exec 2>/dev/null # Redirect stderr to /dev/null, leaving fd 4 alone

# Programs won't show output by default; they write to fd 1 which is now /dev/null.
# This is because programs inherit file descriptors from the shell by default.
echo foo

# You can choose to show messages by having them write to fd 3 (old stdout) instead.
# This works by saying that echo's fd 1 (stdout) is the shell's fd 3.
echo bar >&3

# And when you're done you can reset things to how they were to start with
exec 1>&3 # Point stdout to where fd 3 currently points (the original stdout)
exec 2>&4 # Point stderr to where fd 4 currently points (the original stderr)

exec 3>&- # Close fd 3 (it now points to the same spot as fd 1, so we don't need it)
exec 4>&- # Close fd 4 (it now points to the same spot as fd 1, so we don't need it)

If you're going to use this in a script of any size, and you'll often want to print status updates about how things are going, you'll probably want to create helper functions that do echo "$@" >&3 and echo "$@" >&4 for printing out messages to the original stdout and stderr, so you don't have to pepper references to &3 and &4 all over your script.

And this bash-hackers.org redirection tutorial is a pretty good visual depiction of how moderately complex redirections like these are actually working.

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.