GNU可以使用空格来处理文件名吗?

时间:2012-03-23 11:26:33

标签: shell makefile gnu

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

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) $(foreach)的{​​{3}}表示它返回一个"以空格分隔的名称列表"但完全没有承认这引起的巨大问题。 {{1}}的{​​{3}}也不是。

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

6 个答案:

答案 0 :(得分:49)

错误#712表明make不处理带空格的名称。无处,永远不会。

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

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

因此,使用明确或非常简单的模式规则,您可以使其工作,但除此之外,您运气不好。您将不得不寻找支持空间的其他构建系统。我不确定jam / bjam是否有,sconswafantnantmsbuild都应该有效

答案 1 :(得分:18)

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的有限能力就足够了。

答案 2 :(得分:12)

此方法还允许使用列出的文件名(如$?)和用户变量(即文件列表)。

在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路径,但如果必须使用空格,则转义空格和替换有效。

答案 3 :(得分:4)

如果您愿意再多依赖shell,这会给出一个列表,该列表可以保存带空格的名称:

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

答案 4 :(得分:1)

最初的问题是“重命名不是一种选择”,但许多评论者指出,重命名几乎是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 "$<"。]

答案 5 :(得分:-1)

其他答案已经很好地解决了这个问题。我只是举了一个例子来说明\转义也适用于依赖项。

$ make --version
GNU Make 4.0
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ sed 's/^  /\t/' >Makefile <<'_EOF_'
X = $(shell find -name 'x*' -type f | sed 's/ /\\ /g')
all: $(X)
  head $(X)
_EOF_
$ touch 'x 1'
$ touch 'x 2'
$ make
head ./x\ 2 ./x\ 1
==> ./x 2 <==

==> ./x 1 <==
$