113 votes

Les wildcards récursifs dans GNU make ?

Cela fait longtemps que je n'ai pas utilisé make alors soyez indulgent avec moi...

J'ai un répertoire, flac contenant des fichiers .FLAC. J'ai un répertoire correspondant, mp3 contenant des fichiers MP3. Si un fichier FLAC est plus récent que le fichier MP3 correspondant (ou si le fichier MP3 correspondant n'existe pas), je veux exécuter une série de commandes pour convertir le fichier FLAC en fichier MP3, et copier les balises.

Le hic : je dois chercher dans le flac de manière récursive, et créer les sous-répertoires correspondants dans le répertoire mp3 répertoire. Les répertoires et les fichiers peuvent contenir des espaces dans leurs noms, et sont nommés en UTF-8.

Et je veux utiliser make pour conduire cela.

1 votes

Pourquoi avoir choisi la marque à cette fin ? J'aurais pensé qu'écrire un script en bash serait plus simple.

0 votes

(...ou je pourrais l'écrire en Ruby ou Python). J'aimerais m'amuser avec make au-delà des bases, et c'est un "projet" que j'ai ouvert en ce moment.

4 votes

@Neil, le concept de make comme transformation de système de fichiers basée sur des motifs est la meilleure façon d'aborder le problème original. Peut-être que les implémentations de cette approche ont leurs limites, mais make est plus proche de l'implémentation que la simple bash .

136voto

ndim Points 11557

J'essaierais quelque chose de ce genre

FLAC_FILES = $(shell find flac/ -type f -name '*.flac')
MP3_FILES = $(patsubst flac/%.flac, mp3/%.mp3, $(FLAC_FILES))

.PHONY: all
all: $(MP3_FILES)

mp3/%.mp3: flac/%.flac
    @mkdir -p "$(@D)"
    @echo convert "$<" to "$@"

Quelques notes rapides pour make les débutants :

  • Le site @ devant les commandes empêche make d'imprimer la commande avant de l'exécuter.
  • $(@D) est la partie répertoire du nom du fichier cible ( $@ )
  • Assurez-vous que les lignes contenant des commandes shell commencent par une tabulation et non par des espaces.

Même si cela devrait gérer tous les caractères UTF-8 et autres, cela échouera pour les espaces dans les noms de fichiers ou de répertoires, comme le montre le tableau suivant make utilise des espaces pour séparer les choses dans les makefiles et je ne connais pas de moyen de contourner cela. Il ne vous reste donc qu'un shell script, j'en ai bien peur :-/

0 votes

C'est là où je voulais en venir... les doigts rapides pour la victoire. Bien qu'il semble que vous puissiez faire quelque chose d'intelligent avec... vpath . Je dois étudier cela un de ces jours.

2 votes

Cela ne semble pas fonctionner lorsque les répertoires ont des espaces dans leurs noms.

0 votes

Je n'avais pas réalisé que je devais payer pour find pour obtenir les noms de manière récursive...

76voto

larskholte Points 116

Vous pouvez définir votre propre fonction joker récursive comme ceci :

rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))

Le premier paramètre ( $1 ) est le nom du répertoire, et le second ( $2 ) est le motif que vous voulez faire correspondre.

Exemples

Pour trouver tous les fichiers C dans le répertoire courant :

$(call rwildcard, , *.c)

Pour trouver tous les .c et .h fichiers dans src :

$(call rwildcard, src/, *.c *.h)

Source :

0 votes

Cela ne semble pas fonctionner pour moi. J'ai copié la fonction exacte et elle ne cherche toujours pas de manière récursive.

5 votes

J'utilise GNU Make 3.81, et cela semble fonctionner pour moi. Cela ne fonctionnera pas si l'un des noms de fichiers contient des espaces, cependant. Notez que les noms de fichiers qu'elle renvoie ont des chemins relatifs au répertoire courant, même si vous ne listez que les fichiers d'un sous-répertoire.

3 votes

C'est vraiment un exemple, que make est un Tar Pit de Turing (voir ici : yosefk.com/blog/fun-at-the-turing-tar-pit.html ). Ce n'est même pas si difficile, mais il faut le lire : gnu.org/software/make/manual/html_node/Call-Function.html et ensuite "comprendre la récurrence". VOUS avez dû écrire cela de manière récursive, au sens propre du terme ; ce n'est pas la compréhension courante de "inclure automatiquement les éléments des sous-répertoires". Il s'agit d'une véritable RECURRENCE. Mais rappelez-vous - "Pour comprendre la récurrence, vous devez comprendre la récurrence".

6voto

kenorb Points 2464

Si vous utilisez Bash 4.x, vous pouvez utiliser une nouvelle option de globbing par exemple :

SHELL:=/bin/bash -O globstar
list:
  @echo Flac: $(shell ls flac/**/*.flac)
  @echo MP3: $(shell ls mp3/**/*.mp3)

Ce type de joker récursif peut trouver tous les fichiers qui vous intéressent ( .flac , .mp3 ou autre). O

2voto

Pour info, j'ai utilisé quelque chose comme ça dans un projet de Makefile :

RECURSIVE_MANIFEST = `find . -type f -print`

L'exemple ci-dessus effectuera une recherche à partir du répertoire actuel ( '.' ) pour tous les "fichiers ordinaires" ( "-type f ) et définir le RECURSIVE_MANIFEST faire une variable à chaque fichier qu'il trouve. Vous pouvez alors utiliser des substitutions de motifs pour réduire cette liste, ou alternativement, fournir plus d'arguments dans la variable make. trouver pour réduire ce qu'il retourne. Voir la page de manuel de trouver .

2voto

user3199485 Points 1

Ma solution est basée sur celle ci-dessus, utilise sed au lieu de patsubst pour déformer la sortie de find ET s'échapper des espaces.

Passer de flac/ à ogg/

OGGS = $(shell find flac -type f -name *.flac | sed 's/ /\\ /g;s/flac\//ogg\//;s/\.flac/\.ogg/' )

Mises en garde :

  1. Il est toujours bloqué s'il y a des points-virgules dans le nom du fichier, mais c'est plutôt rare.
  2. L'astuce $(@D) ne fonctionnera pas (produit du charabia), mais oggenc crée des répertoires pour vous !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X