我有这个bash(MacOS上的3.2版)功能
rtf(){
local filter=${1//\*/\(\.\)\*};
local bin="vendor/bin/phpunit --filter="
local command=$bin"'/"$filter"/i'";
echo $command;
$command;
}
它想要做的是使用正确的正则表达式运行phpunit,这里我将*
转换为(.)*
并将过滤器包装为不区分大小写。
现在
$ rtf test*fail
// results in
vendor/bin/phpunit --filter='/test(.)*fail/i'
问题是当我直接在终端运行时,解析的命令工作得很好(匹配的测试用例运行)。但是当函数rtf
试图在最后一行调用它时它不会。不知怎的,phpunit无法找到匹配项。
我可以相信问题在于$command
变量中的正斜杠。可以建议修复吗?
答案 0 :(得分:1)
简短回答:见BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!。
答案很长:将命令(或命令的一部分)放入变量中,然后将它们完整地取出是很复杂的。当shell在命令行上扩展变量时,如果变量是双引号,则它不会被解析;如果它不在引号中,则其中的空格被解析为参数中断,但不解析引号和转义。在任何一种情况下,将引号放在变量的值中没有任何用处 - 它们被视为数据,而不是shell语法。要看到这一点,请考虑一个简单的例子:
$ echo expr '5 * 3'
expr 5 * 3
$ command="expr '5 * 3'"
$ echo $command
expr '5 Desktop Documents Downloads Library Movies Music Pictures Public 3'
注意两个echo
命令之间的输出差异。在第一个中,单引号由shell解析并在字符串传递给echo
之前被删除,因此它们没有被打印出来。在第二个中,单引号被视为数据,传递给echo
,打印出来;另外,由于*
没有引号(就shell而言), 扩展为当前目录中的文件列表。有什么猜测运行$command
会做什么?试试吧:
$ expr '5 * 3'
5 * 3
$ $command
expr: syntax error
...当我直接输入命令时它工作正常,但是从变量执行它失败,因为表达式'5 Desktop Documents Downloads Library Movies Music Pictures Public 3'
无效。
那么,你应该怎么做呢?一种可能性是使用eval
,正如@pynexj建议的那样,但是eval
作为一个臭虫磁铁享有当之无愧的声誉。使用eval
的脚本通常在测试中很有效,但是稍后在某些数据中出现shell元字符时,eval
会识别它并将其包含在解析过程中,然后会出现混乱。所以不要这样做,这是一个坏主意。
你应该怎么做?嗯,通常最好和最简单的选择是跳过存储命令,然后直接执行它。如果要打印等效命令,请单独执行此操作:
rtf(){
local filter=${1//\*/\(\.\)\*}
echo vendor/bin/phpunit --filter="'/$filter/i'"
vendor/bin/phpunit --filter="/$filter/i"
}
请注意,在echo
命令中,我在字符串周围添加了单引号 - 这些并不是命令的一部分,但是它们会被打印出来,从而产生等效命令即将执行的内容。
还有其他选项,包括将命令放在一个数组中,但这似乎有点过分。如果您有兴趣,请参阅BashFAQ #50了解详情。
答案 1 :(得分:-1)
您应该使用eval "$command"
而不是$command
。请参阅以下示例:
[STEP 101] $ cmd="echo 'foo'"
[STEP 102] $ $cmd
'foo'
[STEP 103] $ eval "$cmd"
foo
[STEP 104] $
编写rtf test*fail
并不安全,如果模式与某些文件匹配,则应将rtf "test*fail"
或test*fail
扩展为文件名。