Рекурсивные символы в GNU делают? - PullRequest
80 голосов
/ 20 марта 2010

Прошло много времени с тех пор, как я использовал make, так что терпите меня ...

У меня есть каталог, flac, содержащий файлы .FLAC. У меня есть соответствующий каталог mp3, содержащий файлы MP3. Если FLAC-файл является более новым, чем соответствующий MP3-файл (или соответствующий MP3-файл не существует), тогда я хочу запустить набор команд для преобразования FLAC-файла в MP3-файл и скопировать теги. *

The kicker: мне нужно рекурсивно искать в каталоге flac и создавать соответствующие подкаталоги в каталоге mp3. Каталоги и файлы могут содержать пробелы в именах и именоваться в UTF-8.

И я хочу использовать make для управления этим.

Ответы [ 6 ]

103 голосов
/ 20 марта 2010

Я бы попробовал что-то в этом духе

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 использует пробелы для разделения содержимого в make-файлах, и я не знаю способа обхода тот. Боюсь, так что вам остается только сценарий оболочки: - /

43 голосов
/ 15 августа 2013

Вы можете определить свою собственную рекурсивную функцию подстановки следующим образом:

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)

Источник

1 голос
/ 04 декабря 2011

FWIW, я использовал что-то подобное в Makefile :

RECURSIVE_MANIFEST = `find . -type f -print`

В приведенном выше примере будет выполнен поиск в текущем каталоге ( '.' ) всех "простых файлов" ( '- тип f' ) и задана переменная RECURSIVE_MANIFEST make. на каждый файл, который он находит. Затем вы можете использовать подстановки шаблонов, чтобы уменьшить этот список, или, альтернативно, предоставить больше аргументов в find , чтобы сузить то, что он возвращает. См. Справочную страницу для find .

0 голосов
/ 25 декабря 2016

Вот скрипт на 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])

Конечно, это может быть улучшено для параллельного кодирования. Это оставлено в качестве упражнения для читателя; -)

0 голосов
/ 10 мая 2015

Если вы используете Bash 4.x, вы можете использовать новый параметр глобализации , например:

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

Этот вид рекурсивного подстановочного знака может найти все файлы, которые вас интересуют ( .flac , .mp3 или что-либо еще). О * +1010 *

0 голосов
/ 15 января 2014

Мое решение основано на приведенном выше, использует sed вместо 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 создает для вас каталоги!
...