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


91

使って久しぶりなmakeので我慢して...

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

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

そして、makeこれを駆動するために使用したいと思います。


1
この目的でmakeを選択する理由は何ですか?私はbashスクリプトを書く方が簡単だと思っていたでしょう

4
@Neil、パターンベースのファイルシステム変換としてのmakeのコンセプトは、元の問題に取り組むための最良の方法です。おそらく、このアプローチの実装には限界がありますが、makeそれを裸よりも実装に近いbashです。
Pは

1
@Pavelまあ、shFLACファイル(のリストを歩くスクリプトがfind | while read flacname)、作るmp3nameことからの、上の「MKDIR -p」を実行しdirname "$mp3name"、その後、if [ "$flacfile" -nt "$mp3file"]変換"$flacname"には"$mp3name"本当に魔法ではありません。makeベースのソリューションと比較して実際に失う唯一の機能は、Nファイル変換プロセスをと並行して実行できることですmake -jN
ndim

4
@ndim makeの構文が "nice"と記述されるのを聞いたのはこれが初めてです:-)

1
makeファイル名にスペースを使用することは、相反する要件です。問題のあるドメインに適したツールを使用してください。
イェンス

回答:


115

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

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


これは私が行くところでした...勝利のための速い指。あなたはで賢いことができるように見えますがvpath。これらの日のいずれかを勉強する必要があります。
dmckee ---元モデレーターの子猫2010

1
ディレクトリに名前にスペースが含まれている場合は動作しないようです。
ロジャーリップスコム

find名前を再帰的に取得するために私は殻を剥がさなければならないことを知らなかった...
Roger Lipscombe

1
@PaulKonova:を実行しますmake -jN。以下のためのNコンバージョン数を使用make並行して実行する必要があります。注意:make -jなしで実行すると、Nすべての変換プロセスが同時に並行して開始されます。これは、フォーク爆弾に相当する場合があります。
ndim 2013年

1
@Adrian:この.PHONY: all行は、すべてよりも新しいallというファイルがある場合でも、ターゲットのレシピが実行されることをmakeに伝えます。all$(MP3_FILES)
ndim 2018

52

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

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

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

例:

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

$(call rwildcard,.,*.c)

内のすべての.cおよび.hファイルを検索するにはsrc

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

この関数は、この記事の実装に基づいており、いくつかの改善点があります。


これは私にはうまくいかないようです。私は正確な関数をコピーしましたが、それでも再帰的には見えません。
Jeroen 2013年

3
私はGNU Make 3.81を使用していますが、それでうまくいくようです。ただし、ファイル名にスペースが含まれている場合は機能しません。サブディレクトリ内のファイルのみを一覧表示している場合でも、返されるファイル名には現在のディレクトリからの相対パスがあることに注意してください。
larskholte 2013年

2
これはまさに一例でmakeあり、チューリングタールピットです(ここを参照:yosefk.com/blog/fun-at-the-turing-tar-pit.html)。難しいことではありませんが、gnu.org / software / make / manual / html_node / Call-Function.htmlを読んでから、「繰り返しを理解する」必要があります。あなたは逐語的に、これを再帰的に書かなければなりませんでした。「サブディレクトリからのものを自動的に含める」という日常的な理解ではありません。それは実際の繰り返しです。しかし、覚えておいてください-「再発を理解するには、再発を理解する必要があります」。
Tomasz Gandor 2014

@TomaszGandor再発を理解する必要はありません。再帰を理解する必要があります。そのためには、最初に再帰を理解する必要があります。
user1129682

悪いことに、私は言語の偽りの友達になりました。久しぶりにコメントを編集することはできませんが、皆さんに理解してもらいたいです。また、再帰についても理解しています。
Tomasz Gandor

2

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

RECURSIVE_MANIFEST = `find . -type f -print`

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


1

私の解決策は上記のものに基づいており、スペースのエスケープとエスケープのsed代わりにpatsubstを使用しfindています。

flac /からogg /へ

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

警告:

  1. ファイル名にセミコロンが含まれている場合でもbarfsしますが、かなりまれです。
  2. $(@ D)トリックは機能しません(意味不明な出力)が、oggencはディレクトリを作成します!

1

これは、元の問題を解決するために私がすばやくハッキングした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])

もちろん、これを拡張してエンコードを並行して実行できます。それは読者への練習問題として残します;-)


0

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

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

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


1
私には、$(wildcard flac / ** / *。flac)でも機能するようです。OS X、Gnu Make 3.81
akauppi

2
$(wildcard ./**/*.py)を試しましたが、$(wildcard ./*/*.py)と同じように動作しました。makeが実際に**をサポートしているとは思いません。また、2つの*を並べて使用しても失敗しません。
lahwran 2016年

@lahwran Bashシェルを介してコマンドを呼び出し、globstarオプションを有効にしている場合に必要です。多分あなたはGNU makeまたは何か他のものを使っていません。代わりにこの構文を試すこともできます。コメントでいくつかの提案を確認してください。そうでなければ、それは新しい質問のためのものです。
kenorb 2016年

@kenorbいいえ、私はあなたのことも試していませんでした。なぜなら、私はこの特定のものに対するシェルの呼び出しを避けたかったからです。あかっぴの提案物を使っていました。私が行ったことはlarskholteの答えのように見えましたが、ここのコメントはこれが微妙に壊れていると言ったので、どこかから入手しました。肩をすくめる:)
lahwran 2016年

@lahwran **拡張グロビングはbash / zshであるため、この場合は機能しません。
ケノーブ2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.