-1

I have a readonly variable set in a bashscript:

readonly NO_OUTPUT="1>/dev/null 2>&1"

Why is it, when I use this variable with certain commands in the script, it gives an error like it's thinking that ${NO_OUTPUT} is another file in its list?

For example, I have the following:

make install ${MAKE_PREFIX} ${NO_OUTPUT}

generates an error at the end that says: make: *** No rule to make target '1>/dev/null 2>&1'. Stop.

However, if I change it to:

make install ${MAKE_PREFIX} 1>/dev/null 2>&1

it works exactly as expected?

I have similar issues with other commands (cp, rm, etc) where it errors...

3 Answers3

4
make install ${MAKE_PREFIX} ${NO_OUTPUT}

Calls make with install, the result of split+glob applied to the contents of the MAKE_PREFIX variable and the result of split+glob applied to the contents of the NO_OUTPUT variable as separate arguments.

Probably you want something like:

if [ -n "$VERBOSE" ]; then
  nooutput() { "$@"; }
else
  nooutput() { "$@" > /dev/null 2>&1; }
fi

nooutput make install "$MAKE_PREFIX"

For make to be called with install and the contents of the MAKE_PREFIX variable as separate arguments, and its stdout and stderr redirected to /dev/null if not run in verbose mode.

You could also use aliases here:

if [ -n "$VERBOSE" ]; then
  alias nooutput=' '
else
  alias nooutput='> /dev/null 2>&1 '
fi

nooutput make install "$MAKE_PREFIX"

Though beware if using the bash shell not in POSIX mode, you need shopt -s expand_aliases.

The alias also needs to have been defined at the time the code using it was read (not run).

In zsh, you can also have global aliases that are expanded even when not in command position, so you could do:

if [ -n "$VERBOSE" ]; then
  alias -g '$NOOUTPUT$='
else
  alias -g '$NOOUTPUT$=> /dev/null 2>&1'
fi

make install "$MAKE_PREFIX" $NOOUTPUT$

Here using $NOOUTPUT$ instead of $NOOUTPUT to make it clearer that no parameter expansion is happening there, the $NOOUTPUT$ is being replaced with either nothing or > /dev/null 2>&1 when the code is being read, long before it is being evaluated.

3

The shell separates each word (token, part) on the command line to identify what each part is a long time before expanding each part. Redirections are set-up close to that command line parsing phase. The variable expansion at the much later time becomes only one word which could not become a redirection.

No, not even array variables, or simple variables could work:

$ NO_OUTPUT=( "1>/dev/null"    "2>&1" )
$ printf '%s\n' "${NO_OUTPUT[@]}"
1>/dev/null
2>&1

$ a="1>/dev/null" $ b="2>&1" $ printf '%s\n' "$a" "$b" 1>/dev/null 2>&1

$ printf '%s\n' 1>/dev/null 2>&1

$

So, no, there is no way to convert variable contents to shell word tokens.

Except eval. But eval is usually evil. A security risk.

1

use eval command, it takes a string and runs it as a command, your code should look like this:

eval make install ${MAKE_PREFIX} ${NO_OUTPUT}

on why it happens and also some better ways to get the result you want take a look at How can we run a command stored in a variable.

Amirreza
  • 131
  • 3
    Should rather be: eval 'make install "$MAKE_PREFIX" '"$NO_OUTPUT". i.e., you don't want the expansion of $MAKE_PREFIX to be passed to eval, and you don't want either of these variables to be subject to split+glob. You want eval to evaluated the make install "$MAKE_PREFIX" > /dev/null 2>&1 shell code. – Stéphane Chazelas Sep 07 '22 at 08:39