我想执行一条语句来启动服务器。为此,我有一个环境变量来确定要启动哪个服务器。我得到了这个命令作为起点:
eval "exec gunicorn --chdir /this/dir package.sub:call_main() -b 0.0.0.0:80"
由于我有几种服务器需要启动,我想参数化脚本。经过一番搜索,我发现报价是多余的。所以我现在拥有的是:
APP=main
eval exec gunicorn --chdir /this/dir package.sub:call_${APP}() -b 0.0.0.0:80
然而,这会产生一个syntax error near unexpected token '('
.理想情况下,我什至希望有一个像 这样的默认参数${APP:-main}
,但我想一旦语法错误问题得到解决,这是可能的。
上面的说法有什么问题吗?另外,这里eval
是否需要?exec
答案1
在第二段代码中,您删除了 参数周围的双引号eval
。不要那样做。删除它们将使()
shell 变得特殊(它启动一个子 shell)。
反而:
app=main
eval "exec gunicorn --chdir /this/dir package.sub:call_$app'()' -b 0.0.0.0:80"
eval
在重新评估字符串时,仍然需要在此处引用括号。变量扩展将在调用$app
之前完成。eval
或者,
app=main
eval "exec gunicorn --chdir /this/dir 'package.sub:call_$app()' -b 0.0.0.0:80"
这可能看起来更好。
请注意,除了紧跟在变量名中有效的字符(如)之外,${APP}
和在各方面都是相同的。在这里,不需要。另外,使用小写变量名称以避免与现有环境变量发生意外冲突。$APP
"${APP}x"
{...}
我认为这里不需要eval
或exec
。该字符串似乎不需要用andeval
重新评估exec
代替当前的 shell 进程gunicorn
(我不知道这是否是你想要的)。
可能就足够了
app=main
gunicorn --chdir /this/dir "package.sub:call_$app()" -b 0.0.0.0:80
请注意双引号。
有关的:
答案2
您将执行一项复杂的任务:shell 应该执行 11 次传递(并且每次传递都会破坏输入,以便进行下一步)。
你的代码是
APP=main
eval exec gunicorn --chdir /this/dir package.sub:call_${APP}() -b 0.0.0.0:80
为了理解,删除 eval:
exec gunicorn --chdir /this/dir package.sub:call_${APP}() -b 0.0.0.0:80
注意:第一个示例类似,您删除了eval
和 双引号,因此您仍然得到相同的输出。
现在你有了()
保留的(这是特别的,因为在这种情况下不能以其他方式误解令牌:没有前缀,我们不在命令上下文中(因此调用子shell,使用括号)。注意:这是因为这 11 个步骤,以及标记化的顺序,我们大概就明白为什么会这样了(而且 shell 解析器是很久以前设计的,所以很简单)。
因此,您必须告诉 shell 将 () 传递到下一个解析器步骤。
您可以使用转义(\
):
exec gunicorn --chdir /this/dir package.sub:call_${APP}\(\) -b 0.0.0.0:80
或引用:
exec gunicorn --chdir /this/dir package.sub:call_${APP}'()' -b 0.0.0.0:80
注意:您不需要引用整个“单词”(参数),您可以只引用一部分,这样就不需要单引号${APP}
(需要扩展)。
对于第一个示例,您可以引用双引号:
eval "exec gunicorn --chdir /this/dir \"package.sub:call_main()\" -b 0.0.0.0:80"
这样 eval 将删除外部引号,并且 exec 将看到内部引号(未转义)