109

我已经有一段时间没有使用了make,所以请多多包涵……

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

踢球者:我需要flac递归搜索目录,并在目录中创建相应的子目录mp3。目录和文件的名称可以包含空格,并以 UTF-8 命名。

我想用它make来驱动它。

4

6 回答 6

133

我会尝试这些方面的东西

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)是目标文件名的目录部分 ( $@)
  • 确保其中包含 shell 命令的行以制表符开头,而不是空格。

即使这应该处理所有 UTF-8 字符和内容,它也会在文件或目录名称中的空格处失败,因为make使用空格来分隔 makefile 中的内容,我不知道有什么方法可以解决这个问题。所以恐怕你只剩下一个shell脚本了:-/

于 2010-03-20T13:35:06.317 回答
70

您可以像这样定义自己的递归通配符函数:

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)

此功能基于本文的实现,并进行了一些改进。

于 2013-08-15T17:37:11.633 回答
4

如果您使用的是 Bash 4.x,则可以使用新的 globbing option,例如:

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

这种递归通配符可以找到您感兴趣的所有文件(.flac.mp3或其他文件)。○

于 2015-05-10T18:01:33.423 回答
2

FWIW, I've used something like this in a Makefile:

RECURSIVE_MANIFEST = `find . -type f -print`

The example above will search from the current directory ('.') for all "plain files" ('-type f') and set the RECURSIVE_MANIFEST make variable to every file it finds. You can then use pattern substitutions to reduce this list, or alternatively, supply more arguments into find to narrow what it returns. See the man page for find.

于 2011-12-03T20:09:07.717 回答
2

我的解决方案基于上述解决方案,使用sed而不是破坏AND 转义空格patsubst的输出。find

从 flac/ 到 ogg/

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

注意事项:

  1. 如果文件名中有分号,仍然会出错,但它们很少见。
  2. $(@D) 技巧不起作用(输出乱码),但 oggenc 为您创建目录!
于 2014-01-15T18:52:50.917 回答
2

这是我快速编写的一个 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])

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

于 2016-12-25T19:38:20.270 回答