我在 bashscript 中设置了一个只读变量:
readonly NO_OUTPUT="1>/dev/null 2>&1"
为什么当我在脚本中的某些命令中使用此变量时,它会给出错误,就像它认为 ${NO_OUTPUT} 是其列表中的另一个文件一样?
例如,我有以下内容:
make install ${MAKE_PREFIX} ${NO_OUTPUT}
最后生成一个错误,内容如下: make: *** No rule to make target '1>/dev/null 2>&1'. Stop.
但是,如果我将其更改为:
make install ${MAKE_PREFIX} 1>/dev/null 2>&1
它完全按照预期工作吗?
我对其他命令(cp、rm 等)也有类似的问题,其中出现错误...
答案1
make install ${MAKE_PREFIX} ${NO_OUTPUT}
调用make
时install
,将 split+glob 的结果应用于 MAKE_PREFIX 变量的内容,并将 split+glob 的结果应用于 NO_OUTPUT 变量的内容作为单独的参数。
也许你想要这样的东西:
if [ -n "$VERBOSE" ]; then
nooutput() { "$@"; }
else
nooutput() { "$@" > /dev/null 2>&1; }
fi
nooutput make install "$MAKE_PREFIX"
用于make
将install
MAKE_PREFIX 变量的内容作为单独的参数进行调用,如果不在详细模式下运行,则其 stdout 和 stderr 重定向到 /dev/null 。
您还可以在此处使用别名:
if [ -n "$VERBOSE" ]; then
alias nooutput=' '
else
alias nooutput='> /dev/null 2>&1 '
fi
nooutput make install "$MAKE_PREFIX"
尽管如果bash
在非 POSIX 模式下使用 shell,请注意,您需要shopt -s expand_aliases
.
别名还需要在使用它的代码时定义读(不是跑步)。
在 中zsh
,您还可以有全球的即使不在命令位置也会扩展别名,因此您可以这样做:
if [ -n "$VERBOSE" ]; then
alias -g '$NOOUTPUT$='
else
alias -g '$NOOUTPUT$=> /dev/null 2>&1'
fi
make install "$MAKE_PREFIX" $NOOUTPUT$
这里使用$NOOUTPUT$
而不是为了$NOOUTPUT
更清楚地表明那里没有发生参数扩展,要么$NOOUTPUT$
被替换为空,要么> /dev/null 2>&1
在读取代码时,早在评估代码之前就被替换了。
答案2
shell 在扩展每个部分之前,会在命令行上分隔每个单词(标记、部分),以识别每个部分是什么。重定向是在命令行解析阶段附近设置的。很久以后的变量扩展就变成了一无法成为重定向的单词。
不,甚至数组变量或简单变量也不起作用:
$ 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
$
所以,不,没有办法将变量内容转换为 shell 单词标记。
除了eval
。但 eval 通常是evil
.存在安全风险。
答案3
使用eval
命令,它接受一个字符串并将其作为命令运行,您的代码应如下所示:
eval make install ${MAKE_PREFIX} ${NO_OUTPUT}
关于为什么会发生这种情况以及获得您想要的结果的一些更好的方法,请看一下我们如何运行存储在变量中的命令。