在Mac上的Bash中转发斜​​杠

时间:2017-10-18 14:17:07

标签: php regex bash phpunit

我有这个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变量中的正斜杠。可以建议修复吗?

2 个答案:

答案 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扩展为文件名。