GNU中的递归通配符make?

时间:2010-03-20 13:28:36

标签: makefile gnu-make

自从我使用make以来已经有一段时间了,所以请耐心等待......

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

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

我想用make来推动这一点。

6 个答案:

答案 0 :(得分:101)

我会沿着这些方向尝试一些事情

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脚本,我担心: - /

答案 1 :(得分:42)

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

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

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

实施例

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

$(call rwildcard, , *.c)

要查找.c中的所有.hsrc个文件:

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

Source

答案 2 :(得分:2)

FWIW,我在 Makefile 中使用了类似的东西:

RECURSIVE_MANIFEST = `find . -type f -print`

上面的示例将从当前目录('。')搜索所有“普通文件”(' - type f')并设置{{1为它找到的每个文件创建变量。然后,您可以使用模式替换来减少此列表,或者,在 find 中提供更多参数以缩小它返回的内容。请参阅查找的手册页。

答案 3 :(得分:1)

我的解决方案基于上面的解决方案,使用sed代替patsubst来破坏find的输出并转义空格。

从flac /到ogg /

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

注意事项:

  1. 如果文件名中有分号,仍然是barf,但它们非常罕见。
  2. $(@ D)技巧不起作用(输出乱码),但oggenc为你创建目录!

答案 4 :(得分:0)

如果您正在使用Bash 4.x,则可以使用a new globbing option,例如:

sudo codesign -f -s - Mail.app

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

答案 5 :(得分:0)

这是一个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])

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