【问题标题】:Can GNU make handle filenames with spaces?GNU 可以处理带空格的文件名吗?
【发布时间】:2012-04-07 23:09:26
【问题描述】:

我有一个包含多个文件的目录,其中一些文件的名称中有空格:

Test workspace/
Another directory/
file1.ext
file2.ext
demo 2012-03-23.odp

我在这个目录上使用 GNU 的 $(wildcard) 命令,然后使用 $(foreach) 遍历结果,打印出所有内容。代码如下:

FOO := $(wildcard *)
$(info FOO = $(FOO))
$(foreach PLACE,$(FOO),$(info PLACE = $(PLACE)))

这是我希望看到的打印结果:

Test workspace
Another directory
file1.ext
file2.ext
demo 2012-03-23.odp

这是我实际得到的:

Test
workspace
Another
directory
file1.ext
file2.ext
demo
2012-03-23.odp

后者显然对我没有用。用于$(wildcard)documentation 完全声明它返回一个“以空格分隔的名称列表”,但完全没有意识到这会引发巨大的问题。 documentation 也不是 $(foreach)

是否有可能解决这个问题?如果是这样,怎么做?重命名每个文件和目录以删除空格不是一种选择。

【问题讨论】:

    标签: shell makefile gnu


    【解决方案1】:

    错误#712 表明make 不处理带有空格的名称。无处可去。

    我通过用\ 转义空格找到了blog post saying it's partially implemented\\ 似乎是拼写错误或格式错误),但是:

    • 它不适用于除$(wildcard) 之外的任何函数。
    • 从变量扩展名称列表时不起作用,其中包括特殊变量$?$^$+ 以及任何用户定义的变量。这反过来意味着虽然$(wildcard) 将匹配正确的文件,但您无论如何都无法解释结果。

    因此,使用显式或非常简单的模式规则,您可以使其工作,但除此之外,您就不走运了。您将不得不寻找其他一些支持空间的构建系统。我不确定jam/bjamsconswafantnantmsbuild 是否都可以工作。

    【讨论】:

    • 我得到了$?$@。如果你有兴趣,请看下面我的回答。和路易斯,错了,它确实有效。随意自己测试一下
    【解决方案2】:

    GNU Make 在使用空格分隔的文件名方面做得很差。

    空格在单词列表中到处都是用作分隔符。

    This blog post 很好地总结了情况,但是 警告:它错误地使用了 \\ 而不是 \

    target: some\ file some\ other\ file
    
    some\ file some\ other\ file:
        echo done
    

    你也可以使用变量,所以这也可以

    VAR := some\ file some\ other\ file
    
    target: $(VAR)
    
    $(VAR):
        echo done
    

    只有wildcard 函数可以识别转义,所以你不能做任何花哨的事情而没有很多痛苦。


    但不要忘记你的 shell 也使用空格作为分隔符

    如果我想将 echo done 更改为 touch $@,我必须添加斜杠以将其转义为我的 shell。

    VAR := some\ file
    
    target: $(VAR)
    
    $(VAR):
        touch $(subst \,\\,$@)
    

    或者,更可能的是,使用引号

    VAR := some\ file some\ other\ file
    
    target: $(VAR)
    
    $(VAR):
        touch '$@'
    

    最后,如果您想避免很多痛苦,无论是在 GNU make 中还是在您的 shell 中,都不要在文件名中添加空格。如果你这样做了,希望 Make 的有限功能就足够了。

    【讨论】:

    • “不要在文件名中添加空格”:我从不这样做。但有时你必须编写一个与其他人的源代码一起工作的 make 文件....
    • @Mars,我明白了。做好承受痛苦的准备。
    • 不开玩笑,保罗。我想我花了几个小时尝试不同的事情。我试图通过文件的存在来测试版本。我放弃。现在每个人都必须使用相同的版本。 (在我的情况下这并不难,但这是一个不优雅的解决方案。)
    • 幸运的是,您可以仍然使用$(shell ...) 进行初始操作。
    • 我正在使用 WSL,所以我拥有 Windows 上的所有 linux 工具......并且我所有的二进制文件都在“程序文件”中。达昂。
    【解决方案3】:

    此方法还允许使用列出的文件名,例如 $? 和作为文件列表的用户变量。

    在 Make 中处理空格的最好方法是用空格代替其他字符。

    s+ = $(subst \ ,+,$1)
    
    +s = $(subst +,\ ,$1)
    
    $(call s+,foo bar): $(call s+,bar baz) $(call s+,bar\ baz2)
        # Will also shows list of dependencies with spaces.  
        @echo Making $(call +s,$@) from $(call +s,$?)
    
    $(call s+,bar\ baz):
    
        @echo Making $(call +s,$@)
    
    $(call s+,bar\ baz2):
    
        @echo Making $(call +s,$@)
    

    输出

    Making bar baz
    Making bar baz2
    Making foo bar from bar baz bar baz2
    

    然后您可以使用所有 GNU Make 安全地操作文件名列表 职能。请务必在规则中使用这些名称之前删除 +。

    SRCS := a\ b.c c\ d.c e\ f.c
    
    SRCS := $(call s+,$(SRCS))
    
    # Can manipulate list with substituted spaces
    OBJS := $(SRCS:.c=.o)
    
    # Rule that has object files as dependencies.
    exampleRule:$(call +s,$(OBJS))
        # You can now use the list of OBJS (spaces are converted back).
        @echo Object files: $(call +s,$(OBJS))
    
    a\ b.o:
        # a b.o rule commands go here...
        @echo in rule: a b.o
    
    c\ d.o:
    
    e\ f.o:
    

    输出

    in rule: a b.o
    Object files: a b.o c d.o e f.o
    

    此信息均来自其他所有人发布的blog

    大多数人似乎建议在路径中不使用空格或使用 Windows 8.3 路径,但如果必须使用空格,则转义空格和替换有效。

    【讨论】:

    • 如果你的文件名中突然出现+ 怎么办?
    • 您必须使用文件名中不常见的字符,例如 $$$$ 或 ++++
    【解决方案4】:

    如果你愿意更多地依赖你的 shell,这会给出一个列表,它可以很好地保存带有空格的名称:

    $(shell find | sed 's: :\\ :g')
    

    【讨论】:

      【解决方案5】:

      最初的问题说“重命名不是一种选择”,但许多评论者指出,重命名几乎是 Make 处理空格的唯一方法。我建议一个中间方法:使用 Make 临时重命名文件,然后重命名它们。这为您提供了具有隐式规则和其他优点的 Make 的所有功能,但不会弄乱您的文件命名方案。

      # Make cannot handle spaces in filenames, so temporarily rename them
      nospaces:
          rename -v 's/ /%20/g' *\ *
      # After Make is done, rename files back to having spaces
      yesspaces:
          rename -v 's/%20/ /g' *%20*
      

      您可以使用make nospacesmake yesspaces 手动调用这些目标,或者您可以让其他目标依赖于它们。例如,您可能希望有一个“推送”目标,以确保在将文件同步回服务器之前将空格放回文件名中:

      # Put spaces back in filenames before uploading
      push: yesspaces
          git push
      

      [旁注:我尝试了建议使用 +ss+ 的答案,但它使我的 Makefile 更难阅读和调试。当它让我对隐含的规则大笑时,我放弃了它:%.wav : %.ogg ; oggdec "$<"。]

      【讨论】:

      猜你喜欢
      • 2016-06-07
      • 2012-09-22
      • 2023-03-22
      • 2011-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-02
      相关资源
      最近更新 更多