【问题标题】:Parentheses for subshell don't work when stored in a variable存储在变量中时,子外壳的括号不起作用
【发布时间】:2017-01-22 14:07:49
【问题描述】:

命令:

( echo 1 )

当我在命令行中输入它时工作正常,但如果我将它存储为变量并调用它,它会给出错误:

(echo: command not found

代码:

input="( echo 1 )"
$input

当我这样调用它时,为什么它不以相同的方式评估括号并将其放入子shell中?

【问题讨论】:

  • BashFAQ #50。不带引号的扩展只经过两个解析阶段(字段拆分和全局扩展);这是有充分理由的(否则在 bash 中编写安全代码会困难得多)。链接的常见问题解答包含大量详细信息,包括对您可能希望将代码存储在变量中的情况的最佳实践的讨论。
  • 你不能把这样的命令放在一个变量中。事实上,将命令放入变量中很容易失败。变量包含数据,而不是命令! 如果您想将命令存储在某处以供将来使用,请使用函数。
  • 顺便说一句,你的代码和你的错误没有对齐。如果您使用此处给出的确切代码,您将收到(: command not found。请务必完全按照要求实际复制/粘贴问题中的代码,以避免将来出现此类情况,以防这些差异实际上很重要。

标签: bash shell unix parentheses subshell


【解决方案1】:

BashFAQ #50 对此进行了详细讨论。

不带引号的扩展只经历两个 shell 解析阶段:字段拆分和全局扩展。因此,( echo 1 ) 首先被拆分为字段:(echo1);每个都扩展为一个 glob(没有实际意义,因为它们都不是 glob 扩展);然后它们作为命令运行:( 被调用,第一个参数echo,第二个参数1,第三个参数)

存储代码的正确方法是在函数中:

# best-practices approach
input() ( echo 1; )
input

...或者,如果您想向人类读者更明确地表明您真的想要一个子shell,并且由于错误或习惯而没有使用括号而不是大括号:

# same, but more explicit about intent
input() { (echo 1); }
input

...如果不可能,可以使用eval(但请注意BashFAQ #48 中给出的警告):

# avoid this approach if at all possible
input="( echo 1 )"
eval "$input"

如果您在字符串中构建命令的真正原因是参数化其内容,请改用数组:

input_args=( 1 )                    # define an array
input() ( echo "${input_args[@]}" ) # use that array in a function (if needed)

# add things according to conditional logic as appropriate
if (( 2 > 1 )); then
  input_args+=( "possible argument here" )
fi

# call the function, or just use the array directly, such as: (echo "$(input_args[@]}" )
input

【讨论】:

    【解决方案2】:

    括号是shell语法。

    要将存储在变量中的语法反映返回到shell中进行处理,您必须使用eval命令。

    仅仅将变量的值插入命令行不会触发eval 语法评估。那将是对 eval 的不必要入侵,会导致各种问题。

    例如,考虑一下:

    arg="(parenthesized)"
    somecommand $arg
    

    我们只希望somecommand 以字符串(parenthesized) 作为参数被调用;我们不想运行子shell。这种隐式 eval 会将无害的数据转化为实时代码,从而造成安全漏洞,更不用说试图避免它的编码噩梦了。

    $arg 的处理规则与位置无关;即使$arg 是命令行中的第一个元素,扩展也会以同样的方式发生:

    $arg foo
    

    参数替换将$arg 转换为文本(parenthesized),然后将其作为命令尝试,而不是作为shell 语法。

    要执行存储在input 中的shell 脚本,请使用:

    eval "$input"
    

    你也可以这样做:

    input="echo 1"       # no parentheses
    
    /bin/sh -c "$input"  # run in subshell explicitly
    

    当然,子shell 方法将代码片段提供给执行周围脚本的同一个shell,而这里我们选择了/bin/sh,它可能与调用shell 不同,或者即使它是指向同一个 shell 的符号链接。

    【讨论】:

    • 由于 command 是内置的,因此将其用作 some-random-other-command 的替代品有点令人困惑。
    • @CharlesDuffy 这是一个很好的观点,先生;它必须被修复。
    猜你喜欢
    • 1970-01-01
    • 2014-06-27
    • 2015-03-18
    • 2014-11-23
    • 2014-06-12
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多