命令:
( echo 1 )
在我在命令行输入时工作正常,但是如果我将它存储为变量并调用它,则会出错:
(echo: command not found
代码:
input="( echo 1 )"
$input
为什么它不以相同的方式评估括号,并在我这样称呼时将其放入子shell中?
答案 0 :(得分:2)
BashFAQ #50中详细讨论了这一点。
一个不带引号的扩展只经历了两个shell解析阶段:字段拆分和glob扩展。因此,( echo 1 )
首先被分为以下字段:(
,echo
,1
和)
;每个都被扩展为一个glob(没有实际意义,因为它们都不是glob扩展);然后它们作为命令运行:调用(
,第一个参数为echo
,第二个参数为1
,第三个参数为)
。< / p>
存储代码的正确方法是在函数中:
# best-practices approach
input() ( echo 1; )
input
...或者,如果你想让人类读者更清楚地知道你真的想要一个子壳而不是因为错误或习惯而使用parens而不是括号: / p>
# 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
答案 1 :(得分:0)
括号是shell语法。
要将存储在变量中的反映语法反馈回shell进行处理,必须使用eval
命令。
仅将变量的值插入命令行不会触发eval
语法评估。这将是对eval的不必要的入侵,这将导致各种各样的问题。
例如,考虑一下:
arg="(parenthesized)"
somecommand $arg
我们只希望以字符串somecommand
作为参数调用(parenthesized)
;我们不想运行子shell。这种隐式评估会将无害数据转化为实时代码,从而产生安全漏洞,更不用说编码噩梦以试图避免它。
如何处理$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的符号链接。