Makefile、内置方括号、变量扩展和命令替换

Makefile、内置方括号、变量扩展和命令替换

为了遵循 DRY(不要重复自己)原则,我有时需要在生成文件。所以该文件中的某处有一个食谱,例如:

shell=/bin/bash
# …
.ONESHELL:
run:
    # Create bash sub-shell «cmd» var, holding a built-in test as a string
    @cmd='[ $$(grep -iE _dev </etc/hosts | wc -l) -eq 0 ]'
    # "$$" tells make to escape the dollar sign. So echoing …
    @echo "$$cmd"
    # gives «[ $(grep -iE _dev </etc/hosts | wc -l) -eq 0 ]» as I expected
    # I need this variable to be expanded and interpreted as a real
    # built-in square bracket test, so I wrote
    @$$cmd && echo "Pass ! do more magical things …" || true

我期望制作转义$符号$$cmd$cmd又将在巴什将上下文放入括号测试不带引号的字符串...对吗?
但我得到了一个错误/bin/bash: line 2: [: too many arguments

有人知道为什么会出现这个错误吗?
为什么 bash 没有给出我期望的括号测试?
[ $(grep -iE _dev </etc/hosts | wc -l) -eq 0 ] && echo "Pass!"

谢谢。

答案1

shell 中的变量和命令替换是在决定命令边界并解析重定向(和赋值)之后发生的。您可以将程序名称和/或参数放入变量中并替换它们,但不能使用管道或重定向或其他替换,包括$( command )赋值或 shell 关键字(如ifand )for

在这种情况下,您可以通过更改命令并反转测试来消除管道和 wc 和替换:

cmd='grep -qi _dev /etc/hosts' # note file as argument not redirection
$$cmd || do_blah

如果替换的 grep 命令在文件中找不到任何匹配项,则它会失败(静默),如果失败则执行 do_blah。

一般来说,要在替换值中使用 shell 语法(不仅仅是程序参数),您必须使用它eval来执行替​​换值(或多个值),或者运行子 shell sh -c "$$cmd"(如果需要,根据环境和/或命令替换其他 shell) )。

答案2

AMakefile不是 shell 脚本。整个事情可以更简单、更干净地写为

#!/bin/sh
set -e
grep -q -iE _dev /etc/hosts
echo pass
...

相关内容