web-dev-qa-db-fra.com

Est-ce que GNU peut gérer les noms de fichiers avec des espaces?

J'ai un répertoire contenant plusieurs fichiers, dont certains ont des espaces dans leurs noms:

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

J'utilise la commande $(wildcard) de GNU sur ce répertoire, puis j'itère le résultat en utilisant $(foreach), en imprimant tout. Voici le code:

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

Voici ce que je m'attendrais à voir imprimé:

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

Voici ce que j'obtiendrais réellement:

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

Ce dernier ne me sert évidemment à rien. Le documentation for $(wildcard) flat-out déclare qu'il retourne une "liste de noms séparés par des espaces" mais échoue complètement à reconnaître les énormes problèmes que cela pose. Ni documentation pour $(foreach).

Est-il possible de contourner cela? Si c'est le cas, comment? Renommer chaque fichier et répertoire pour supprimer les espaces n'est pas une option.

67
qntm

Le bug # 712 suggère que make ne gère pas les noms avec des espaces. Nulle part, jamais.

J'ai trouvé un article de blog disant qu'il est partiellement implémenté en échappant aux espaces avec \ (\\ Semble être une faute de frappe ou un formatage), mais:

  • Il ne fonctionne dans aucune fonction à l'exception de $(wildcard).
  • Cela ne fonctionne pas lorsque vous développez des listes de noms à partir de variables, ce qui inclut les variables spéciales $?, $^ Et $+ Ainsi que toute variable définie par l'utilisateur. Ce qui à son tour signifie que même si $(wildcard) correspondra aux fichiers corrects, vous ne pourrez pas interpréter le résultat de toute façon.

Ainsi, avec des règles de modèle explicites ou très simples, vous pouvez le faire fonctionner, mais au-delà de cela, vous n'avez pas de chance. Vous devrez chercher un autre système de construction qui prend en charge les espaces. Je ne sais pas si jam / bjam le fait, scons , waf , ant , nant et msbuild tout devrait fonctionner.

49
Jan Hudec

GNU Make fait très mal avec les noms de fichiers séparés par des espaces.

Les espaces sont utilisés comme délimiteurs dans la liste Word partout.

Ce billet de blog résume bien la situation, mais ATTENTION: il utilise incorrectement \\ plutôt que\

target: some\ file some\ other\ file

some\ file some\ other\ file:
    echo done

Vous pouvez également utiliser des variables, donc cela fonctionnerait également

VAR := some\ file some\ other\ file

target: $(VAR)

$(VAR):
    echo done

Seule la fonction wildcard reconnaît l'échappement, vous ne pouvez donc rien faire de fantaisie sans beaucoup de douleur.


Mais n'oubliez pas que votre Shell utilise également des espaces comme délimiteurs .

Si je voulais changer le echo done à touch $@, Je devrais ajouter une barre oblique pour lui échapper pour mon Shell.

VAR := some\ file

target: $(VAR)

$(VAR):
    touch $(subst \,\\,$@)

ou, plus probablement, utilisez des guillemets

VAR := some\ file some\ other\ file

target: $(VAR)

$(VAR):
    touch '$@'

En fin de compte, si vous voulez éviter beaucoup de douleur, à la fois dans GNU make et dans votre Shell, ne mettez pas d'espaces dans vos noms de fichiers. Si vous le faites, espérons les capacités limitées de Make sera suffisant.

18
Paul Draper

Cette méthode permet également d'utiliser les noms de fichiers répertoriés tels que $? et les variables utilisateur qui sont des listes de fichiers.

La meilleure façon de gérer les espaces dans Make est de remplacer les espaces par d'autres personnages.

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,$@)

Les sorties

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

Vous pouvez ensuite manipuler en toute sécurité les listes de noms de fichiers en utilisant toutes les fonctions GNU Make. Assurez-vous simplement de supprimer les + avant d'utiliser ces noms dans une règle.

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:

Les sorties

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

Ces informations proviennent toutes du blog que tout le monde publiait.

La plupart des gens semblent recommander de ne pas utiliser d'espaces dans les chemins ou d'utiliser des chemins Windows 8.3, mais si vous devez utiliser des espaces, les espaces d'échappement et les travaux de substitution.

13
Mr_Moneybags

Si vous êtes prêt à compter un peu plus sur votre Shell, cela donne une liste qui peut contenir des noms avec des espaces très bien:

$(Shell find | sed 's: :\\ :g')
4
benzaita

La question d'origine disait que "renommer n'est pas une option", mais de nombreux commentateurs ont souligné que renommer est à peu près la seule façon dont Make peut gérer les espaces. Je suggère une solution intermédiaire: utilisez Make pour renommer temporairement les fichiers, puis renommez-les. Cela vous donne toute la puissance de Make avec des règles implicites et d'autres avantages, mais ne gâche pas votre schéma de nommage de fichiers.

# 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*

Vous pouvez appeler ces cibles à la main avec make nospaces et make yesspaces, ou vous pouvez avoir d'autres cibles en dépend. Par exemple, vous voudrez peut-être avoir une cible "Push" qui s'assure de remettre les espaces dans les noms de fichiers avant de synchroniser les fichiers avec un serveur:

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

[Sidenote: j'ai essayé la réponse qui suggérait d'utiliser +s et s+ mais cela a rendu mon Makefile plus difficile à lire et à déboguer. Je l'ai abandonné quand il m'a donné un coup de gueule sur les règles implicites comme: %.wav : %.ogg ; oggdec "$<".]

1
hackerb9

D'autres réponses couvrent déjà assez bien le problème. Je viens de faire un exemple pour montrer que le \- l'échappement fonctionne également pour les dépendances.

$ 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 <==
$
0
Kirill Bulygin