自从我使用了一段时间以来make
,请忍受...
我有一个目录flac
,其中包含.FLAC文件。我有一个对应的目录,mp3
其中包含MP3文件。如果FLAC文件比相应的MP3文件新(或相应的MP3文件不存在),那么我想运行一堆命令将FLAC文件转换为MP3文件,并在其中复制标签。
缺点:我需要flac
递归搜索目录,并在mp3
目录中创建相应的子目录。目录和文件的名称中可以有空格,并以UTF-8命名。
我想用make
这个来驱动。
自从我使用了一段时间以来make
,请忍受...
我有一个目录flac
,其中包含.FLAC文件。我有一个对应的目录,mp3
其中包含MP3文件。如果FLAC文件比相应的MP3文件新(或相应的MP3文件不存在),那么我想运行一堆命令将FLAC文件转换为MP3文件,并在其中复制标签。
缺点:我需要flac
递归搜索目录,并在mp3
目录中创建相应的子目录。目录和文件的名称中可以有空格,并以UTF-8命名。
我想用make
这个来驱动。
make
比裸露更接近于实现它bash
。
sh
遍历flac文件列表(find | while read flacname
)的脚本,mp3name
从中生成一个,在上运行“ mkdir -p” dirname "$mp3name"
,然后if [ "$flacfile" -nt "$mp3file"]
将其"$flacname"
转换"$mp3name"
为并不是真正的魔术。与make
基于基础的解决方案相比,您实际上失去的唯一功能是可以与N
并行运行文件转换过程make -jN
。
make
在文件名中使用空格并带有空格是矛盾的要求。使用适合于问题域的工具。
Answers:
我会沿着这些思路尝试
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脚本了:-/
vpath
。这几天必须学习一下。
find
make -jN
。对于N
使用的转换次数make
应该并行运行。注意:如果make -j
不使用,N
则会立即并行启动所有转换过程,这可能相当于叉子炸弹。
.PHONY: all
行告诉makeall
即使要存在all
比所有文件都更新的文件,也要执行目标的配方$(MP3_FILES)
。
您可以定义自己的递归通配符函数,如下所示:
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
第一个参数($1
)是目录列表,第二个参数()是$2
您要匹配的模式列表。
要查找当前目录中的所有C文件:
$(call rwildcard,.,*.c)
要查找中的所有.c
和.h
文件src
:
$(call rwildcard,src,*.c *.h)
此功能基于本文的实现,并进行了一些改进。
make
就是图灵焦油坑(请参见此处:yosefk.com/blog/fun-at-the-turing-tar-pit.html)。它甚至没有那么难,但是必须阅读以下内容:gnu.org/software/make/manual/html_node/Call-Function.html,然后“了解重复发生”。您必须逐字逐句地编写此代码;这不是“自动包括子目录中的内容”的日常理解。这是实际的递归。但是请记住-“要了解复发,就必须了解复发”。
这是我快速破解的Python脚本,用于解决原始问题:保留音乐库的压缩副本。该脚本会将.m4a文件(假定为ALAC)转换为AAC格式,除非AAC文件已经存在并且比ALAC文件新。库中的MP3文件将被链接,因为它们已经被压缩。
请注意,中止脚本(ctrl-c)将留下半转换的文件。
我最初也想编写一个Makefile来处理这个问题,但是由于它不能处理文件名中的空格(请参阅接受的答案),并且因为编写bash脚本肯定会让我感到痛苦,所以确实如此。它非常简单明了,因此很容易调整您的需求。
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])
当然,可以增强它以并行执行编码。留给读者练习;-)
如果您使用的是Bash 4.x,则可以使用新的globlob选项,例如:
SHELL:=/bin/bash -O globstar
list:
@echo Flac: $(shell ls flac/**/*.flac)
@echo MP3: $(shell ls mp3/**/*.mp3)
这种递归通配符可以找到您感兴趣的所有文件(.flac,.mp3或其他文件)。Ø
**
不起作用,因为扩展的globing是bash / zsh的事情。