How to assign redirection symbol to a variable for

2020-07-17 07:40发布

问题:

I have a couple of BASH scripts that launch programs I commonly use with common arguments using variables for the command and arguments. The invocation appears at the end like so:

$PROGRAM $ARG1 $ARG2 &

Now I want to redirect output to stderr and stdout to /dev/null by default. I want to be able to disable that with a switch to the script (-v = verbose). But if I try to assign "2>&1 > /dev/null" to a var, say REDIRECTION, (clearing it if verbose is specified) and try to invoke like so:

$PROGRAM $ARG1 $ARG2 $REDIRECTION &

The redirection directives are passed as arguments to the program. Is there a way to do this? Or do I have to use my solution which is to have 2 separate invocation lines, one with and one without the redirection directives, depending on "verbosity"?

回答1:

One option is to use the eval builtin, which processes its arguments like a Bash command, handling things like redirection operators. However, eval is pretty risky, since it will reprocess all of its arguments, even ones that have already been processed properly, and this can cause bizarre or unsafe behavior if not done perfectly.

Since the only problematic difference between the two versions of your command is the presence or absence of redirections, a better option is to use the exec builtin. This command:

exec >/dev/null 2>&1

redirects the STDOUT and STDERR of the remainder of a shell-script to /dev/null. This command:

[[ "$IS_VERBOSE" ]] || exec >/dev/null 2>&1

will run exec >/dev/null 2>&1 unless $IS_VERBOSE is non-blank, in which case it does nothing. (You can replace [[ "$IS_VERBOSE" ]] with whatever sort of conditional expression you're already using to detect verbosity.)

So, what you'd want is something like this:

(
    [[ "$IS_VERBOSE" ]] || exec >/dev/null 2>&1
    "$PROGRAM" "$ARG1" "$ARG2" &
)

The parentheses ( ... ) are to set up a subshell, so the exec command (if run) only affects up until the ).

(By the way, note that I changed your 2>&1 > /dev/null to > /dev/null 2>&1: the order matters.)



回答2:

Rather than trying to encode shell syntax into parameter, just put the default destination for each redirection operator in a parameter, and allow a method for overriding the default.

# A little bash-specific, but appropriate defaults should exist for the shell
# of your choice.
STDOUT=/dev/stdout
STDERR=/dev/stderr

if some-test; then
    STDOUT=some-other-file
fi

if some-other-test; then
    STDERR=different-file
fi

$PROGRAM $ARG1 $ARG2 > $STDOUT 2> $STDERR


回答3:

You can build any command line you like and then use eval to execute it:

line="echo hello"
if ....
    line="$line > junk.out"
fi

eval $line

(BUt I would probably go with the 2-line approach you suggest as being easier to read and understand...)