我有一些时间没有使用make
了,所以请耐心等待...
我有一个名为flac
的目录,其中包含FLAC文件。我有一个对应的目录mp3
,其中包含MP3文件。如果FLAC文件比相应的MP3文件新(或者相应的MP3文件不存在),那么我想运行一些命令将FLAC文件转换为MP3文件,并复制标签。
重点是:我需要递归搜索flac
目录,并在mp3
目录中创建相应的子目录。这些目录和文件名中可能会有空格,并且以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新手的一些快速提示:
@
可以防止make
在实际运行之前打印该命令。$(@D)
是目标文件名($@
)的目录部分。即使这应该处理所有UTF-8字符和其他东西,但它在文件或目录名称中的空格上会失败,因为make
在makefile中使用空格来分隔内容,我不知道有没有方法可以解决这个问题。所以这只留给你一个shell脚本,我很抱歉 :/
find
е‘Ҫд»ӨжқҘйҖ’еҪ’иҺ·еҸ–ж–Ү件еҗҚгҖӮ - Roger Lipscombemake -jN
。对于N
,请使用make
应该并行运行的转换数量。注意:在没有N
的情况下运行make -j
将同时启动所有转换进程,这可能相当于一个fork bomb。 - ndim.PHONY: all
这一行告诉 make,即使存在一个比所有 $(MP3_FILES)
都新的名为 all
的文件,也要执行 all
目标的配方。 - ndim您可以像这样定义自己的递归通配符函数:
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
第一个参数 ($1
) 是目录列表,第二个参数 ($2
) 是您希望匹配的模式列表。
要在当前目录中查找所有C文件:
$(call rwildcard,.,*.c)
查找 src
中所有的 .c
和 .h
文件:
$(call rwildcard,src,*.c *.h)
这个函数基于这篇文章的实现,经过了一些改进。make
是图灵陷阱(参见这里:http://yosefk.com/blog/fun-at-the-turing-tar-pit.html)。它并不难懂,但人们需要阅读这个链接:https://www.gnu.org/software/make/manual/html_node/Call-Function.html ,然后“理解循环”。你必须以递归的方式编写代码,按照字面意思来说;这不是“从子目录自动包含文件”的日常理解。这是真正的循环,但请记住——“要理解循环,你必须理解循环”。 - Tomasz Gandor$(sort $(dir $(call rwildcard,src,*)))
(其中 src
是我的顶级目录)。 - RodneySHELL:=/bin/bash -O globstar
list:
@echo Flac: $(shell ls flac/**/*.flac)
@echo MP3: $(shell ls mp3/**/*.mp3)
**
不起作用,因为扩展的 globbing 是 bash/zsh 特有的功能。 - kenorbFWIW,我在Makefile中使用了类似这样的东西:
RECURSIVE_MANIFEST = `find . -type f -print`
上面的示例将从当前目录('.')搜索所有“普通文件”('-type f'),并将RECURSIVE_MANIFEST
make变量设置为它找到的每个文件。然后,您可以使用模式替换来缩小此列表,或者通过向find提供更多参数来缩小其返回内容。请参阅find的man页面。
sed
而不是patsubst
来处理find
命令的输出,并转义空格。OGGS = $(shell find flac -type f -name "*.flac" | sed 's/ /\\ /g;s/flac\//ogg\//;s/\.flac/\.ogg/' )
注意事项:
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])
find
),你可以使用函数。然后像另一个答案中那样使用结果来转换文件。rwildcard=$(wildcard $1) $(foreach d,$1,$(call rwildcard,$(addsuffix /$(notdir $d),$(wildcard $(dir $d)*))))
FLAC_FILES = $(call rwildcard,flac/*.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 "$@"
sh
脚本遍历 flac 文件列表 (find | while read flacname
),从中制作出一个mp3name
,在"$mp3name"
上运行 "mkdir -p",然后,如果[ "$flacfile" -nt "$mp3file"]
,将"$flacname"
转换为"$mp3name"
并不是真正的魔法。与基于make
的解决方案相比,您实际上失去的唯一功能是无法使用make -jN
并行运行N
个文件转换进程的可能性。 - ndimmake
并且文件名中有空格是矛盾的要求。请使用适合该问题领域的工具。 - Jens