【问题标题】:Does make not support backtick notation?make 不支持反引号吗?
【发布时间】:2020-06-23 00:17:31
【问题描述】:

场景

我在尝试将命令中的值分配给 makefile 中的变量时遇到了一些麻烦。使用反引号符号似乎可以正常工作,但只要我使用更现代的 $(pwd) 符号,变量的值就不会返回任何内容。

$ cat Makefile
FOO=moo
BAR=$(pwd)
BAZ=`pwd`

all: foo bar baz

foo: 
    @echo foo:$(FOO)';'
bar:
    @echo bar:$(BAR)';'
baz:
    @echo baz:$(BAZ)';'
$ make all
foo:moo;
bar:;
baz:/home/mazunki;

如您所见,FOO 和 BAZ 按预期工作。但不是酒吧。

问题

现在让我们通过添加更多示例来看看我的问题。正如你所知道的那样,我正在跳过回声。

$ cat Makefile
SHELL=/bin/env bash

FOO=dog
BAR=$(pwd)
BAZ=`pwd`
QUZ=`$(FOO)`/cat
ZOT=`\`FOO\``/owl
BIM=`$(BAZ)`/rat
BAM=`\`BAZ\``/pig
BUM=`BUM`/fox

all: foo bar baz quz zot bim bam bum
# targets for all the things

$ make
foo:dog;
bar:;
baz:/home/mazunki;
bash: dog: command not found
quz:/cat;
bash: FOO: command not found
zot:/owl;
bim:pwd/rat;
bash: BAZ: command not found
bam:/pig;
bash: BAZ: command not found
bum:/fox;

我真的对 BIM 抱有希望。但遗憾的是,它只返回我想运行的命令。我尝试向其添加另一层$() 和/或``,但结果却是空输出。

我想要的输出是在 Bash 中运行 echo -n $(pwd)/rat 得到的。我的测试示例都不允许我这样做。我希望能够做到这一点,因为我用于序言变量的一些命令是基于初始设置的,例如:

# User settings
ROOT=`pwd`
SRCPATH=$(ROOT)/src/
OBJPATH=$(ROOT)/objects/

# Scripting
SRCFILES=`find $(SRCPATH) -name '*.ext' -printf '%p '`
OBJFILES=`find $(SRCPATH) -name '*.ext' -printf '$(OBJPATH)/%p ' | sed 's/^$(SRCPATH)/^$(OBJPATH)//g; s/.ext;/.obj;/g; s/;$//g'`

这可能看起来有点复杂,但我这样做有两个主要原因:我希望它具有高度可配置性,无需自己编辑命令,还因为我想学习如何制作 Makefile正确。

【问题讨论】:

    标签: bash makefile


    【解决方案1】:

    不,Makefile 不支持这种方式的反引号。 Makefile 不是 shell 脚本,根本不与 bash 共享语法。

    您的 BAZ 示例似乎只起作用,因为反引号最终被逐字复制到 shell,因此 Make 变量 BAZ 包含 `pwd`不是 /home/mazunki),当注入到 shell 命令中时,会导致 shell 运行echo `pwd`。 Make 从来没有以任何方式解释反引号。

    要从 make 调用 shell 命令,您可以使用 $(shell ...):

    var := $(shell pwd)
    foo:
            @echo $(var)
    

    请注意,该命令也是一个 Make 表达式,而不是直接的 shell 命令,因此您要传递给底层 shell 的任何 $s 都必须进行转义。

    【讨论】:

    • Makefiles 不是 Bash 的事实确实回答了这个问题。这很令人困惑,因为它们共享相同的语法,因为 ``` ` ``` 和 $() 都是该语言的一部分。是否有添加完整 Bash 支持的初衷,还是有其他原因?无论如何,你的答案是完美的。
    • 反引号不是 Make 的一部分,它们只是文字。 $(...) 做别的事情。例如,Make $(VAR) 类似于 Bash ${VAR},而 Make $(shell CMD) 类似于 Bash $(CMD)
    • 我接受这个答案是因为可以更清楚地立即看到答案,尽管下面的答案很好地解释了为什么它不起作用。注意:$(shell ...) 等于 $$(...),在实践中可能更容易使用。
    • 我必须测试一下:似乎${FOO} 与Make 中的$(FOO) 等效且有效,它的解析和运行似乎相似。有什么区别吗?
    • $(shell ...) 绝对不等于$$(...)。前者由make 评估,而后者是传递给shell 的转义字符串$(...)。我以前在 make 中什至没见过 ${},但手册页上说它与 $() 相同。
    【解决方案2】:

    Make 不支持反引号。 Makefile 不是 shell 脚本。但是,makefile 会运行 shell 脚本,并且 shell 脚本支持反引号。你写了这个:

    $ cat Makefile
    FOO=moo
    BAR=$(pwd)
    BAZ=`pwd`
    
    all: foo bar baz
    
    foo: 
            @echo foo:$(FOO)';'
    bar:
            @echo bar:$(BAR)';'
    baz:
            @echo baz:$(BAZ)';'
    $ make all
    foo:moo;
    bar:;
    baz:/home/mazunki;
    

    变量赋值只是创建包含这些文字字符串的变量,因此FOO 包含字符串mooBAR 包含字符串$(pwd)BAZ 包含字符串`pwd`。

    Make 只替换以$ 开头的变量(也许您已经知道为什么您的makefile 不能按您的意愿工作)。没有什么特别的。因此,当 make 运行 foo 的配方时,它将用其值 moo 替换 make 变量 $(FOO) 并运行 shell 命令 echo foo:moo';'。当 make 运行 baz 的配方时,它会将 $(BAZ) 替换为 `pwd` 并运行 shell 命令:

    echo baz:`pwd`';'
    

    shell(不是 make)会为你扩展反引号。

    但是,当 make 运行 bar 的配方时,它会将 $(BAR) 替换为值 $(pwd),这是另一个 make 变量,它将被扩展,并且这个 make 变量 pwd 没有设置,所以make 将其扩展为空字符串并运行 shell 命令echo bar:';'

    如果您想对 shell 脚本使用新式 $(...) 语法,您必须转义 $,因此 make 不会将其视为 make 变量。你必须写:

    BAR=$$(pwd)
    

    那么它会按照你的预期工作。

    您真的应该阅读https://www.gnu.org/software/make/manual/html_node/Reference.html 和相关页面。

    【讨论】:

    • 哦,$$(...) 用于 bash-things,$(...) 用于 make-things。伟大而简单。他们选择使用相同的符号有什么原因吗?它们是文字字符串这一事实解释了为什么它们不起作用。
    • make 创建时,早在 1970 年代,shell 的 $(...) 语法并不存在。只有反引号存在。 $(...) shell 功能直到很久以后才被发明出来。到那时改变 make 的语法已经太晚了。
    • 为了清楚起见,您必须使用$$ 表示要发送到外壳的任何 美元符号,而不仅仅是$(...)。如果你想引用一个shell变量,比如$$HOME,或者你想在一个正则表达式中使用它,你必须使用它,比如grep 'foo$$'等。
    • 因为否则它会在 make 中出现语法错误,就像在 bash 中用反斜杠(如 echo "foo\")结束字符串一样,实际上永远不会结束字符串。在 Make 中,它将等待其美元符号的参数,在您的情况下为单个 '
    • 注意,这不是 make 中的语法错误。这将是配方中的语法错误,它会发送到 shell 并由 shell 报告。从技术上讲,shell 不会等待任何东西:它不是交互式的。它只会报告语法错误。
    【解决方案3】:

    要通过https://stackoverflow.com/users/1899640 扩展答案https://stackoverflow.com/a/60628899,添加一个工作示例:

    SHELL=/bin/env bash
    
    FOO=$(shell pwd)
    BAR=$(FOO)/src
    BUZ=ext
    
    all: foo bar baz
    
    foo: 
        @echo foo:$(FOO)';'  # returns /home/mazunki
    bar:
        @echo bar:$(BAR)';'  # returns /home/mazunki/src
    baz:
        find $(BAR) -name '*.$(BUZ)'  # returns all files /home/mazunki/src/**/*.ext
    

    【讨论】:

    • 请注意,您在 make 中使用 $(CURDIR) 而不是 $(shell pwd)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 2020-06-25
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    相关资源
    最近更新 更多