web-dev-qa-db-ja.com

GNU make?の再帰的なワイルドカード

makeを使用してからしばらく経ちました。

.FLACファイルを含むflacディレクトリがあります。 MP3ファイルを含むmp3という対応するディレクトリがあります。 FLACファイルが対応するMP3ファイルよりも新しい(または対応するMP3ファイルが存在しない)場合、FLACファイルをMP3ファイルに変換するためのコマンドを実行し、タグをコピーします。

キッカー:flacディレクトリを再帰的に検索し、mp3ディレクトリに対応するサブディレクトリを作成する必要があります。ディレクトリとファイルには名前にスペースを含めることができ、UTF-8で名前が付けられます。

そして、makeを使用してこれを駆動します。

81
Roger Lipscombe

私はこれらの線に沿って何かをしようとします

_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はスペースを使用してメイクファイル内のものを分離するため、ファイル名またはディレクトリ名のスペースで失敗し、回避方法はわかりませんそれ。そのため、シェルスクリプトだけが残ります。

103
ndim

次のように、独自の再帰的なワイルドカード関数を定義できます。

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

最初のパラメーター($1)はディレクトリ名で、2番目のパラメーター($2)は照合するパターンです。

現在のディレクトリですべてのCファイルを見つけるには:

$(call rwildcard, , *.c)

src内のすべての.cおよび.hファイルを見つけるには:

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

ソース

44
larskholte

FWIW、私はこのようなものをMakefileで使用しました:

RECURSIVE_MANIFEST = `find . -type f -print`

上記の例では、現在のディレクトリ('。')からすべての「プレーンファイル」('-type f')を検索し、RECURSIVE_MANIFEST見つかったすべてのファイルに変数を作成します。次に、パターン置換を使用してこのリストを減らすか、またはfindにさらに引数を指定して、返されるものを絞り込むことができます。 findのマニュアルページを参照してください。

2

私の解決策は上記のものに基づいており、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はディレクトリを作成します!
1
user3199485

以下は、元の問題を解決するためにすばやくハッキングしたPythonスクリプトです。音楽ライブラリの圧縮コピーを保持します。 AACファイルが既に存在し、ALACファイルよりも新しい場合を除き、スクリプトは.m4aファイル(ALACと想定)をAAC形式に変換します。ライブラリ内の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])

もちろん、これはエンコードを並行して実行するように拡張できます。それは読者への演習として残されています;-)

1
Brecht Machiels

Bash 4.xを使用している場合は、 新しいグロビングオプション を使用できます。例:

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

この種の再帰的なワイルドカードは、関心のあるすべてのファイルを見つけることができます(。flac。mp3または何でも)。 O

0
kenorb