如何将bash函数参数作为命令进行评估,并考虑可能的环境覆盖?

时间:2017-08-08 17:55:02

标签: bash shell

如何在bash中编写一个函数(我可以依赖它是v4 +),那么,给定构成具有可能环境覆盖的命令的单词,在当前shell中执行此命令?

例如,给定

f cd src
f CXX="ccache gcc" make -k XOPTIONS="--test1 --test2"

函数f与在shell脚本中只有这些行而没有预先f的情况大致相同?

一些不成功的尝试。 这会尝试将环境覆盖CXX="ccache gcc"评估为命令。

f() { "$@" ; }

这会丢失所有参数的词语,在空格上打破单个参数词:

f() { eval "$@" ; }

这会处理环境覆盖,但在子shell中运行命令,因为env(1)不是内置的bash:

f() { env -- "$@" ; }

这个问题多次出现在SO和Unix SE上,但我从未见过它要求支持所有三个重要部分,即:环境覆盖;在当前shell中执行;并正确处理包含空格的参数(以及对bash特有的词汇特征的其他字符)。

我可能会使用的一件事是环境覆盖很少与内置版本一起使用(但是v。IFS= read...),因此我可以基于{"@" ;eval -- "@" ;模式进行选择{1}}在语法上是一个变量赋值。但同样,这并不像在其中发现$1那么简单,因为等号可能被引用为命令的一部分,尽管这不太可能是理智的。尽管如此,我通常更喜欢正确的代码来大多数正确的代码,这种方法有两个连续的信念飞跃。

解决一个可能的问题为什么我需要一个复制shell 的默认行为的函数("只需删除 = "):实际上,f更复杂,只是运行命令,在几十个位置实现脚本中重复的模式;这只是我无法做到的部分。

2 个答案:

答案 0 :(得分:2)

如果你能让eval正确引用你的论据,它应该有效。为此,您可以使用%q的{​​{1}}格式规范,其工作方式如下:

printf

这会产生类似

的功能
$ printf '%q ' CXX="ccache gcc" make -k XOPTIONS="--test1 --test2"
CXX=ccache\ gcc make -k XOPTIONS=--test1\ --test2

请注意,这会在命令末尾附加一个额外的空格,但这不应该受到伤害。

答案 1 :(得分:0)

棘手。你可以这样做,但它会污染shell的环境:

f() {
    # process any leading "var=value" assignments
    while [[ $1 == ?*=* ]]; do
        declare -x "$1"
        shift
    done
    "$@"
}

刚做了一个快速测试:在函数中声明的env变量仍然是函数范围的本地变量,并且实际上不会污染脚本的环境。

$ f() {
  declare -x FOO=bar
  sh -c 'echo subshell FOO=$FOO'
  echo function FOO=$FOO
}
$ unset foo
$ f
subshell FOO=bar
function FOO=bar
$ echo main shell FOO=$FOO
main shell FOO=