【问题标题】:How to avoid both GNU Make and shell from unwrapping the commas and apostrophes when defining variables定义变量时如何避免 GNU Make 和 shell 解开逗号和撇号
【发布时间】:2021-01-25 14:56:45
【问题描述】:

我的 makefile 中有一个名为 SELF 的变量,我想通过 -D 选项将它传递给我的 GCC 编译器来定义一个宏。这个宏在 [0-9a-zA-Z] 中只能有一个字符-但这本身就是另一个问题,我不会在这里讨论-。 我很难找到一种解决方法来避免 GNU Make 和 shell 删除定义中的逗号。 它的定义示例如下:

#include <stdio.h>

#define str(x) #x
#define xstr(x) str(x)
    
#ifndef SELF  
# define SELF '0'
#endif
#pragma message("SELF is: " xstr(SELF))

int main(void){
    return 0;
}

makefile 如下所示:

CC = gcc
WFLAGS = -Wall -Wextra

ifdef SELF
DSELF = -DSELF=$(SELF)
endif
CFLAGS = -std=c99 $(DSELF)

all: clean test

%: %.o

.PHONY: clean
clean:
    -rm -v test

如果您使用make 执行makefile,将出现一个注释,将标记SELF 的定义:

test.c:9:9: note: ‘#pragma message: SELF is: '0'’
    9 | #pragma message("SELF is: " xstr(SELF))
      |         ^~~~~~~

另一方面,执行make SELF='0' 会破坏撇号,正如您在 GNU Make 的详细输出中所见:

gcc -std=c99 -DSELF=0    test.c   -o test

如果您尝试用引号将撇号括起来,例如 make SELF="'0'",则定义将传递给 GCC,但即使是外壳程序也会删除撇号,因为 #pragma 消息仍将显示 0。但是如果直接执行gcc -std=c99 -DSELF="'0'" test.c -o test,定义就成功了:

test.c:9:9: note: ‘#pragma message: SELF is: '0'’
    9 | #pragma message("SELF is: " xstr(SELF))
      |         ^~~~~~~

如何避免这一切发生?这是否意味着您不能定义相同类型的字符串?

【问题讨论】:

  • 我自己可能已经回答了。如果您将: DSELF = -DSELF=$(SELF) 替换为: DSELF = -DSELF=\'$(SELF)\' 您写入“make SELF=”的任何内容都将用撇号定义,但如果您一开始没有写撇号或引号。

标签: c makefile


【解决方案1】:

您的问题非常令人困惑,因为一开始您在谈论逗号,然后您开始谈论完全不同的撇号。

在计算机语言中,我们通常将' 字符称为“单引号”,而不是撇号,它通常指完整字符集中的不同字符。

无论如何,由于 shell 和不关心逗号,我只能假设您在整个问题中的意思是单引号。

答案是,make 根本不关心引号,也不会删除它们。但是,shell 非常关心它们并将为您剥离它们:重要的是要了解 shell 在它启动程序之前删除引号 ,因此程序甚至没有机会知道是否引号存在与否。举你的第一个例子:

make SELF='0'

在这里,shell 将在调用 make 之前删除引号。 Make 无法知道您运行的是 make SELF=0 还是 make SELF='0',因为在调用 make 时,引号已被删除。

然后,当 make 运行 shell 命令来启动编译器时,该 shell 命令将在调用编译器之前删除引号。所以你有一个这样的命令可以运行:

gcc -std=c99 -DSELF='0'    test.c   -o test

shell 删除引号,编译器将得到参数-DSELF=0

您如何防御这种情况?您需要转义引号。在您的情况下,您希望保留单引号,因此您需要在它们周围使用反斜杠或双引号(如您所见)。

不清楚你想做什么,但假设你希望能够像这样运行 make:

make SELF=0

那么您需要在 makefile 中添加两个单引号并将它们转义。 您可以像这样将它们放入您的 makefile 中:

ifdef SELF
DSELF = -DSELF="'$(SELF)'"
endif
CFLAGS = -std=c99 $(DSELF)

现在编译器将使用-DSELF="'0'" 调用,然后shell 将删除外引号,您的程序将使用-DSELF='0' 调用,这正是您想要的。

【讨论】:

  • 这非常快。感谢您的回答,很抱歉破坏了程序员的词汇,因为我不是以英语为母语的人。我的意思是在整个问题中谈论“单引号”和“双引号”。我也确实忘记了转义字符的概念,直到我按下提交问题的几秒钟后。我确定这将是其他地方的重复问题。
猜你喜欢
  • 1970-01-01
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-22
  • 1970-01-01
  • 2018-10-20
  • 1970-01-01
相关资源
最近更新 更多