3

I'm trying to come up with a way script to pass a silent flag in a bash so that all output will be directed to /dev/null if it is present and to the screen if it is not.

An MWE of my script would be:

#!/bin/bash

# Check if silent flag is on.    
if [ $2 = "-s" ]; then
    echo "Silent mode."
    # Non-working line.
    out_var = "to screen"
else
    echo $1
    # Non-working line.
    out_var = "/dev/null"
fi

command1 > out_var

command2 > out_var

echo "End."

I call the script with two variables, the first one is irrelevant and the second one ($2) is the actual silent flag (-s):

./myscript.sh first_variable -s

Obviously the out_var lines don't work, but they give an idea of what I want: a way to direct the output of command1 and command2 to either the screen or to /dev/null depending on -s being present or not.

How could I do this?

Gabriel
  • 40,504
  • 73
  • 230
  • 404
  • 2
    There are no spaces around `=` in shell assignments. That being said and assuming you want the default standard output location if you replace `"to screen"` with `/dev/stdout` and use `$out_var` in the redirections I believe your script should work correctly. – Etan Reisner Sep 03 '14 at 02:21
  • http://www.tldp.org/LDP/abs/html/io-redirection.html is also interesting reading. – Todd A. Jacobs Sep 03 '14 at 02:30

4 Answers4

5

You can use the naked exec command to redirect the current program without starting a new one.

Hence, a -s flag could be processed with something like:

if [[ "$1" == "-s" ]] ; then
    exec >/dev/null 2>&1
fi

The following complete script shows how to do it:

#!/bin/bash

echo XYZZY

if [[ "$1" == "-s" ]] ; then
    exec >/dev/null 2>&1
fi

echo PLUGH

If you run it with -s, you get XYZZY but no PLUGH output (well, technically, you do get PLUGH output but it's sent to the /dev/null bit bucket).

If you run it without -s, you get both lines.

The before and after echo statements show that exec is acting as described, simply changing redirection for the current program rather than attempting to re-execute it.


As an aside, I've assumed you meant "to screen" to be "to the current standard output", which may or may not be the actual terminal device (for example if it's already been redirected to somewhere else). If you do want the actual terminal device, it can still be done (using /dev/tty for example) but that would be an unusual requirement.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    I offered an alternative viewpoint, but I think `exec` is the right way to go for the OP. However, in longer scripts exec might have surprising consequences with pipes or if one wants to send output back to the original sink. Good answer! – Todd A. Jacobs Sep 03 '14 at 02:23
1

There are lots of things that could be wrong with your script; I won't attempt to guess since you didn't post any actual output or errors.

However, there are a couple of things that can help:

  1. You need to figure out where your output is really going. Standard output and standard error are two different things, and redirecting one doesn't necessarily redirect the other.

  2. In Bash, you can send output to /dev/stdout or /dev/stderr, so you might want to try something like:

    # Send standard output to the tty/pty, or wherever stdout is currently going.
    cmd > /dev/stdout
    
    # Do the same thing, but with standard error instead.
    cmd > /dev/stderr
    
  3. Redirect standard error to standard output, and then send standard output to /dev/null. Order matters here.

    cmd 2>&1 > /dev/null
    

There may be other problems with your script, too, but for issues with Bash shell redirections the GNU Bash manual is the canonical source of information. Hope it helps!

Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
0

If you don't want to redirect all output from your script, you can use eval. For example:

$ fd=1
$ eval "echo hi >$a" >/dev/null

$ fd=2
$ eval "echo hi >$a" >/dev/null
hi

Make sure you use double quotes so that the variable is replaced before eval evaluates it.

whereswalden
  • 4,819
  • 3
  • 27
  • 41
0

In your case, you just needed to change out_var = "to screen" to out_var = "/dev/tty". And use it like this command1 > $out_var (see the '$' you are lacking)

I implemented it like this

# Set debug flag as desired
DEBUG=1
# DEBUG=0

if [ "$DEBUG" -eq "1" ]; then
  OUT='/dev/tty'
else
  OUT='/dev/null'
fi

# actual script use commands like this
command > $OUT 2>&1

# or like this if you need
command 2> $OUT

Of course you can also set the debug mode from a cli option, see How do I parse command line arguments in Bash?

And you can have multiple debug or verbose levels like this

# Set VERBOSE level as desired
# VERBOSE=0
VERBOSE=1
# VERBOSE=2

VERBOSE1='/dev/null'
VERBOSE2='/dev/null'

if [ "$VERBOSE" -gte 1 ]; then
  VERBOSE1='/dev/tty'
fi

if [ "$VERBOSE" -gte 2 ]; then
  VERBOSE2='/dev/tty'
fi

# actual script use commands like this
command > $VERBOSE1 2>&1

# or like this if you need
command 2> $VERBOSE2
Community
  • 1
  • 1
jperelli
  • 6,988
  • 5
  • 50
  • 85