我有一个复杂的脚本,它从文件中获取变量并使用它们来运行程序(特别是Wine)
从另一个文件中的变量传递选项无法按预期工作:
#!/bin/bash
. settingsfile
wine $run
在另一个文件中:
run="run.exe -withoption \"This text\""
当我将wine $run
更改为echo wine $run
时,它会回显一个字符串,当明确运行时,它会正常工作:
#!/bin/bash
. settingsfile
wine run.exe -withoption "This text"
修改:使用#!/bin/bash -x
进行操作会向我显示:
+ wine run.exe -withoption '"This' 'text"'
我该如何解决这个问题?
答案 0 :(得分:8)
问题在于"This
和text"
被视为单独的参数,每个参数都包含双引号,而不是单个参数This text
。如果你编写一个函数来打印每行一个参数,你可以看到这个;这样:
function echo_on_separate_lines ()
{
local arg
for arg in "$@" ; do
echo "<< $arg >>"
done
}
run="run.exe -withoption \"This text\""
echo_on_separate_lines $run
打印出来:
<< run.exe >>
<< -withoption >>
<< "This >>
<< text" >>
而不是:
<< run.exe >>
<< -withoption >>
<< This text >>
最简单的解决方案是使用eval
来重新处理引用:
run="run.exe -withoption \"This text\""
wine $run # or better yet: wine "$run"
但更强大的解决方案是让run
成为数组,然后您可以将其称为"${run[@]}"
:
run=(run.exe -withoption "This text")
wine "${run[@]}"
以便从一开始就正确处理报价。
答案 1 :(得分:5)
简短回答:见BashFAQ #050: I'm trying to put a command in a variable, but the complex cases always fail!
答案很长:将引号放在变量中没有任何用处,因为bash在用它们的值替换变量之前解析引号;这意味着当它被$run
替换为run.exe -withoption "This text"
时,它已经完成引用解析,并且不会再回过头来注意那些双引号,这意味着它们不要做你期望的事。
BTW,使用echo
来检查发生的事情是非常误导的,因为echo
会在解析后显示的参数,但是你正在阅读就好像它在解析之前显示它们一样。相反,使用@ Eduardo关于-x
的建议来了解真正发生的事情。 (顺便说一句,您也可以使用set -x
为脚本中有趣的部分启用该功能,然后set +x
将其关闭。)
那你怎么解决呢?通常,最好的方法是将命令放在数组而不是简单变量中,然后使用惯用语"${arrayname[@]}"
将每个数组元素作为单独的参数传递:
run=(run.exe -withoption "This text")
wine "${run[@]}"