Рекурсивные подстановочные знаки в GNU make?
это было время, так как я использовал make
, Так что потерпите меня...
у меня есть каталог, flac
, содержащий .FLAC файлы. У меня есть соответствующий каталог, mp3
содержит MP3 файлы. Если файл FLAC является более новым, чем соответствующий MP3-файл (или соответствующий MP3-файл не существует), то я хочу запустить кучу команд для преобразования файла FLAC в MP3-файл и скопировать теги.
Кикер: мне нужно искать
6 ответов:
Я бы попробовал что-то в этом роде
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
новичков:
- The
@
перед командами предотвращаетmake
от печати команды до ее фактического выполнения.$(@D)
является частью каталога имени целевого файла ($@
)- убедитесь, что строки с командами оболочки в них начинаются с вкладки, а не с пробелов.
даже если это должен обрабатывать все символы UTF-8 и прочее, он не будет работать в пробелах в именах файлов или каталогов, как
make
использует пробелы для разделения материала в makefiles, и я не знаю, как это обойти. Так что у вас остается только сценарий оболочки, я боюсь : -/
вы можете определить свою собственную рекурсивную подстановочную функцию следующим образом:
rwildcard=$(foreach d,$(wildcard *),$(call rwildcard,$d/,) $(filter $(subst *,%,),$d))
первый параметр (
) - это имя каталога, а второе (
) - это шаблон, который вы хотите, чтобы соответствовать.
примеры
чтобы найти все файлы C в текущем каталоге:
$(call rwildcard, , *.c)
найти все
.c
и.h
файлыsrc
:$(call rwildcard, src/, *.c *.h)
FWIW, я использовал что-то вроде этого в Makefile:
RECURSIVE_MANIFEST = `find . -type f -print`
приведенный выше пример будет искать в текущем каталоге ('.- ) для всех "текстовые файлы" ( '- тип f') и
RECURSIVE_MANIFEST
сделать переменную для каждого файла, который он находит. Затем вы можете использовать замены шаблонов, чтобы уменьшить этот список, или, альтернативно, предоставить больше аргументов в найти чтобы сузить то, что он возвращает. Смотрите man-страницу для найти.
мое решение основано на одном выше, использует
sed
вместоpatsubst
калечить выходfind
и избежать пробелов.переход от flac / к ogg/
OGGS = $(shell find flac -type f -name "*.flac" | sed 's/ /\ /g;s/flac\//ogg\//;s/\.flac/\.ogg/' )
предостережения:
- все еще блевотина, если в имени файла есть точки с запятой, но они довольно редки.
- трюк $(@D) не будет работать (выводит тарабарщину), но oggenc создает каталоги для вас!
вот скрипт 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])
конечно, это может быть улучшено для выполнения кодирования параллельно. Это оставлено в качестве упражнения для читателя ; -)
Если вы используете Bash 4.X, вы можете использовать новый подстановка параметра, например:
SHELL:=/bin/bash -O globstar list: @echo Flac: $(shell ls flac/**/*.flac) @echo MP3: $(shell ls mp3/**/*.mp3)
такой рекурсивный подстановочный знак может найти все интересующие вас файлы (.Флак, .mp3 или что-то еще). O