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)
要在src
find所有的.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/' )
注意事项:
- 如果在文件名中有分号,仍然是barfs,但是它们很less。
- $(@ 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])
当然,这可以被增强以并行执行编码。 这是作为一个练习留给读者;-)