为了遵循 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 关键字(如if
and )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
...