GNU make中的recursion通配符?

我已经使用过一段时间了,所以忍耐着我…

我有一个目录, flac ,包含.FLAC文件。 我有一个相应的目录, mp3包含MP3文件。 如果一个FLAC文件比相应的MP3文件(或者相应的MP3文件不存在)更新,那么我想运行一堆命令将FLAC文件转换为MP3文件,然后复制标签。

踢球者:我需要recursion地searchflac目录,并在mp3目录中创build相应的子目录。 目录和文件名称中可以有空格,并以UTF-8命名。

而我想用make来驱动这个。

我会尝试沿着这些线

 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 "$@" 

一些快速注意make初学者:

  • 命令前面的@可以防止在实际运行之前打印命令。
  • $(@D)是目标文件名( $@ )的目录部分
  • 确保其中包含shell命令的行以选项卡开头,而不是以空格开头。

即使这应该处理所有的UTF-8字符和东西,它会在文件或目录名称空间失败,因为make使用空格来分隔makefiles中的东西,我不知道有一种方法来解决这个问题。 所以,只留下一个shell脚本,恐怕: – /

你可以像这样定义你自己的recursion通配符函数:

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

第一个参数( $1 )是目录名称,第二个( $2 )是你想匹配的模式。

例子

要查找当前目录中的所有C文件:

 $(call rwildcard, , *.c) 

要在srcfind所有的.c.h文件:

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

资源

FWIW,我在Makefile中使用了这样的东西:

 RECURSIVE_MANIFEST = `find . -type f -print` 

上面的示例将从当前目录( '。' )中search所有“纯文件”( '-type f' ),并将RECURSIVE_MANIFEST makevariables设置为find的每个文件。 然后,您可以使用模式replace来减less此列表,或者, 可以查找中提供更多参数以缩小返回的范围。 查看手册页查找

我的解决scheme是基于上面的一个,使用sed而不是patsubst来调整find的输出和转义的空间。

从flac /到ogg /

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

注意事项:

  1. 如果在文件名中有分号,仍然是barfs,但是它们很less。
  2. $(@ D)技巧将不起作用(输出乱码),但oggenc为您创build目录!

您可以使用扩展的通配符,例如:

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

这种recursion通配符可以find您感兴趣的所有文件( .flac.mp3或其他)。 Ø

下面是一个Python脚本,我很快就破解了原来的问题:保留一个音乐库的压缩副本。 脚本会将.m4a文件(假定为ALAC)转换为AAC格式,除非AAC文件已经存在并且比ALAC文件更新。 库中的MP3文件将被链接,因为它们已经被压缩。

只要注意放弃脚本( ctrl-c )就会留下一个半转换的文件。

我本来也想编写一个Makefile来处理这个问题,但是由于它不能处理文件名中的空格(参见接受的答案),而且因为编写一个bash脚本将保证让我处于一个痛苦的世界,所以它就是Python。 这是相当直接和简短的,因此应该很容易调整到您的需求。

 from __future__ import print_function import glob import os import subprocess UNCOMPRESSED_DIR = 'Music' COMPRESSED = 'compressed_' UNCOMPRESSED_EXTS = ('m4a', ) # files to convert to lossy format LINK_EXTS = ('mp3', ) # files to link instead of convert for root, dirs, files in os.walk(UNCOMPRESSED_DIR): out_root = COMPRESSED + root if not os.path.exists(out_root): os.mkdir(out_root) for file in files: file_path = os.path.join(root, file) file_root, ext = os.path.splitext(file_path) if ext[1:] in LINK_EXTS: if not os.path.exists(COMPRESSED + file_path): print('Linking {}'.format(file_path)) link_source = os.path.relpath(file_path, out_root) os.symlink(link_source, COMPRESSED + file_path) continue if ext[1:] not in UNCOMPRESSED_EXTS: print('Skipping {}'.format(file_path)) continue out_file_path = COMPRESSED + file_path if (os.path.exists(out_file_path) and os.path.getctime(out_file_path) > os.path.getctime(file_path)): print('Up to date: {}'.format(file_path)) continue print('Converting {}'.format(file_path)) subprocess.call(['ffmpeg', '-y', '-i', file_path, '-c:a', 'libfdk_aac', '-vbr', '4', out_file_path]) 

当然,这可以被增强以并行执行编码。 这是作为一个练习留给读者;-)