マクロ (Macros)
マクロは、指定された変数を置換することによってファイルパスを生成するテンプレート文字列です。マクロはシチュエーションテンプレートやディレクトリ定義で使用されます。
Note
このページでは、プロジェクトシステムによって使用される ファイルパスマクロ (file-path macros) について解説します。テキストパラメータ内の {name} 置換など、ワークフロー内部で作成・参照する名前付きの値については、ワークフロー変数 を参照してください。
完全な構文の詳細に入る前に、マクロが実際にどのように機能するかを示す 2 つの例を挙げます:
テンプレート: {outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension}
すべての変数が提供された場合:
outputs="outputs", node_name="ImageGen", file_name_base="render", _index=2, file_extension="png"
→ outputs/ImageGen_render002.png
オプショナル変数が省略された場合:
outputs="outputs", file_name_base="render", file_extension="png"
→ outputs/render.png
{outputs} は、プロジェクトシステムが自動的に提供するディレクトリ名です。{node_name?:_} はオプショナル(任意指定)です — 値が存在する場合は値の後に _ が付加され、存在しない場合はブロック全体が完全に消去されます。{_index?:03} もオプショナルであり、存在する場合は 3 桁にゼロ埋めされます。
変数構文リファレンス (Variable syntax reference)
必須変数 (Required variable)
{variable_name}
この変数は必ず提供される必要があります。マクロ解決時に存在しない場合、解決はエラーで失敗します。
オプショナル変数 (Optional variable)
{variable_name?}
? は変数をオプショナル(任意)としてマークします。変数が提供されなかった場合、{} ブロック(および付随するフォーマット指定)は出力から完全に除外されます。マクロの残りの部分は通常通り処理されます。
末尾形式 (Trailing form): ? は最後のフォーマット指定の末尾に配置することも可能です — {shot:upper?} は {shot?:upper} と同等です。どちらの表記でも変数がオプショナルとしてマークされます。プレーンな変数でもシーケンス略記でも同様に適用されます:{###:upper?} は {###?:upper} と同じです。? をリテラル文字として維持したい場合は、セパレータをクォート({shot:'lower?'})してください。
セパレータ形式 (Separator format)
{variable_name:separator}
変数の値の末尾に separator を付加します。認識されているキーワード(後述の文字列変換を参照)および数値パディング以外のテキストは、すべてセパレータとして扱われます。
これは、変数が存在しないときに綺麗に消去されるパスプレフィックスを構築するのに最も便利です。例えば、{node_name?:_} はノード名が判明している場合にファイル名の前に node_name_ を追加しますが、判明していない場合は何も出力しません:
{node_name?:_}{file_name_base}
node_name="ImageGen", file_name_base="render" → ImageGen_render
node_name 未提供, file_name_base="render" → render
パスセパレータも同様に機能します — {sub_dirs?:/} はサブディレクトリが指定された場合にのみサブディレクトリプレフィックスを追加します:
{outputs}/{sub_dirs?:/}{file_name_base}.{file_extension}
sub_dirs="lighting/pass_a", file_name_base="render", file_extension="exr"
→ outputs/lighting/pass_a/render.exr
sub_dirs 未提供, file_name_base="render", file_extension="exr"
→ outputs/render.exr
先頭セパレータ (Leading separator)
{variable_name:^prefix}
セパレータ形式 の鏡となる構文ですが、テキストが変数値の末尾ではなく 先頭に付加 されます。フォーマット指定テキストの先頭にある ^ でマークされ、^ 以降のすべてがリテラルプレフィックス文字列となります。変数が値を出力する場合にのみレンダリングされます — 未バインドのオプショナル変数は、先頭セパレータ諸共に出力から消去されます。
{file_name_base}{version?:^_v}.{file_extension}
file_name_base="render", version=3, file_extension="png" → render_v3.png
file_name_base="render", version 未提供 → render.png
代表的な使用パターン: シーケンススロット と組み合わせて、シーケンスの進行に合わせて現れたり消えたりするバージョンサフィックスを作成できます:
render{###?:^_v}.png
初回保存 (スロット省略) → render.png
2回目保存 (スロット発動) → render_v001.png
3回目保存 → render_v002.png
ファイル名以外でも機能します — 末尾セパレータがパスプレフィックス専用でないのと同様に、先頭セパレータも汎用的に使用できます:
Hello, {name?}!{intro?:^ Nice to meet you.}
name="Alice", intro="y" → Hello, Alice! Nice to meet you.y
name="Alice", intro なし → Hello, Alice!
name なし, intro なし → Hello, !
合成ルール
- 1 つの変数に設定できる先頭セパレータは最大 1 つ です。
- 先頭セパレータは、テンプレート内の記述順序に関係なく、同一変数の他のすべてのフォーマット指定の 後 に適用されます。
{shot:03:^_v}と{shot:^_v:03}は、どちらもshot=5を_v005としてレンダリングします — パーサーは先頭指定をリストの末尾に正規化するため、順序によってプレフィックスが崩れることはありません。
関連する構文エラー
| エラー | 原因 |
|---|---|
EMPTY_LEADING_SEPARATOR |
キャレットの後に文字列がない :^ |
MULTIPLE_LEADING_SEPARATORS |
同一変数に 2 つの :^ 指定が存在する |
制限事項: フォーマット指定の先頭にある ^ は先頭セパレータの識別子として予約されているため、現在のところリテラルの ^_v プレフィックスを表現することはできません。現実的なユースケースが生じた場合、将来のエスケープ機構(例: \^ やクォート形式 '^')で対応される予定です。
数値パディング (Numeric padding)
{variable_name:03}
値を指定された桁数にゼロ埋め(パディング)します。変数は整数値を保持している必要があります。
{_index:03} _index = 5 の場合 → "005"
{_index:04} _index = 12 の場合 → "0012"
create_new 衝突ポリシーのもとで自動インクリメントされるファイル名に使用されます。単一の未解決変数に対する数値パディング(:NN)がオプトインのトリガーとなります:初回保存時はインデックス 1(オプショナル形式の場合は省略)となり、以降の保存では同一テンプレートに対してインデックスが進みます — パディング形式はシーケンス全体で維持されます。
- オプショナル形式
{_index?:03}— 初回保存時は省略され、衝突時に_001、_002、… となります(パディング桁数は維持)。 - 必須形式
{_index:03}— 初回保存時から存在します:シーケンス全体で一貫したゼロ埋め桁数で_001、_002、_003、… となります。
テンプレート: {file_name_base}_v{_index:03}.{file_extension}
保存 #1 → render_v001.png
保存 #2 → render_v002.png
保存 #3 → render_v003.png
変数名は必ずしも _index である必要はありません;:NN パディングを持つ単一の未解決必須変数であれば自動的に割り当てられます。パディングがない未解決の必須変数はバインド漏れ(設定エラー)として扱われ保存が失敗します — これにより、ユーザーが結線し忘れた際に {shot} が勝手に 1, 2, 3, … で埋められてしまうのを防ぎます。
シーケンススロット ({###})
{#} → 最小 1 桁 (1, 2, ..., 9, 10, 11, ...)
{###} → 最小 3 桁 (001, 002, ..., 999, 1000, ...)
{####} → 4 桁最小 (0001, 0002, ..., 9999, 10000, ...)
{##?} → 2 桁最小、オプショナル (初回保存時は省略、衝突時に 01, 02, …)
{} 中括弧内の 連続した # 文字は、シーケンススロットの明示的な構文です。各 # は 最小 レンダリング幅に 1 桁寄与します。10 ^ width 未満の値はその幅にゼロ埋めされ、それ以上の値は自然な幅でレンダリングされます(切り捨てられません)。これは ffmpeg (%03d)、Houdini ($F4)、Nuke (####)、および Python の :03 フォーマット指定の普遍的な ### 慣例に一致します。
中括弧内の末尾の ?(例: {##?})は、他の変数と同様にスロットを オプショナル としてマークします。オプショナルスロットは初回保存時には省略され、衝突時にのみ値が入ります。
テンプレート: {file_name_base}_v{###}.{file_extension}
保存 #1 → render_v001.png
保存 #2 → render_v002.png
...
保存 #999 → render_v999.png
保存 #1000 → render_v1000.png (オーバーフロー: 4 桁、切り捨てなし)
テンプレート (オプショナル): {file_name_base}{##?}.{file_extension}
保存 #1 → render.png (スロット省略)
保存 #2 → render01.png (衝突時にスロット発動)
保存 #3 → render02.png
システムによって割り当てられるシーケンスインデックスが必要な場合は、常に {###} を使用してください。これは上述の数値パディングヒューリスティックに頼ることなく「このスロットこそが create_new が衝突時に進めるべきものである」と明示するため、ユーザーがバインドした {shot:03} 変数を純粋に必要とするマクロ作成者も曖昧さなく記述できます。
なぜ {} で囲むのか: マクロテンプレートは、生の # 文字が別の意味を持つ場所(Markdown ヘッダー、コメント、シェルスクリプトなど)によく現れます。記号を {} 内にカプセル化することで、「これはマクロ変数である」と示す既存の区切り文字の中にシーケンススロット構文を収めることができ、静的テキスト内の余計な # 文字に対するエスケープルールが不要になります。
1 マクロあたり 1 つのシーケンススロット: 2 つの {###} ブロックを含むテンプレート(例: {###}_take_{##}.png)は、パース時に拒否されます — システムはどちらのスロットを自動割り当てすべきか判断できないためです。2 つ目の数値が必要な場合は、明示的な {var} として構成してください。
{_index:NN} との関係性: 内部的には、{###} はシーケンスフォーマットマーカーを保持する _index という名前に脱糖(desugar)されます。従来の {_index:03} / {_index?:03} 構文も下位互換性のために引き続き機能し、シーケンススロットとして扱われますが、今後は {###} の使用が推奨されます。プロジェクトテンプレートの移行が進んだ後の将来のバージョンでは、{_index:NN} の略記は廃止される可能性があります(issue #4902 を参照)。
未解決のシーケンススロット (Unresolved sequence slots)
必須の {###} スロットは、書き込みパスが値を割り当てるまで値を持ちません。その割り当てが実行される 前 にマクロを解決するコード(出力の保存先をプレビューするノード、ユーザー入力を絶対パスか相対パスか分類する UI など)は、空のスロットをどう処理するかリゾルバに指示する必要があります。GetPathForMacroRequest はこの選択肢を unresolved_sequence_slot_behavior として公開しており、その値は UnresolvedSequenceSlotBehavior 列挙型に定義されています:
| 挙動 | レンダリング結果 | 使用すべき場面 |
|---|---|---|
FAIL (デフォルト) |
MISSING_REQUIRED_VARIABLES 失敗 |
書き込みパス — この失敗は、on_write_file_request が最初のインデックスをシードし、衝突時に再試行するために使用するシグナルです。他の処理がこのデフォルトを上書きすべきではありません。 |
RENDER_SEQUENCE_PATTERN |
###(またはソース幅に合わせた ####) |
表示専用。スロットを生のハッシュ記号(ffmpeg / Houdini / Nuke の普遍的慣例)としてレンダリングし、結果のパスがディスク上の形状として読めるようにします。このパターンは有効なファイルシステムパスではないため、この文字列を開いたり、書き込んだり、I/O プリミティブに渡したりしないでください。 |
START_AT_ZERO |
000 |
初回保存前に 0 始まりのシーケンスをプレビューする場合。 |
START_AT_ONE |
001 |
「初回保存時にどこに保存されるか」をプレビューする場合 — 書き込みパスのシード値と一致するため、保存先が空であればプレビューが実際の保存結果と一致します。 |
オプショナルスロット({###?})は影響を受けません — 未バインド時はすでに省略されているため、このフラグは必須スロットにのみ効果を発揮します。
経験則: ファイルを開こうとしているコードであればフラグを渡さず、書き込みパスに任せてください。ユーザーに文字列を表示しようとしているコードであれば、RENDER_SEQUENCE_PATTERN を使用してください。START_AT_ZERO / START_AT_ONE は、実際の初回保存をプレビューするための限定的なツールです。
文字列変換 (String transformations)
| フォーマット指定 | 説明 | 結果例 |
|---|---|---|
:lower |
すべて小文字 | "my autumn shoot" |
:upper |
すべて大文字 | "MY AUTUMN SHOOT" |
:title |
タイトルケース | "My Autumn Shoot" |
:snake |
スネークケース (snake_case) | "my_autumn_shoot" |
:pascal |
パスカルケース (PascalCase) | "MyAutumnShoot" |
:camel |
キャメルケース (camelCase) | "myAutumnShoot" |
:screaming_snake |
大文字スネークケース (SCREAMING_SNAKE_CASE) | "MY_AUTUMN_SHOOT" |
:slug |
スラッグ(スペース→ハイフン、英数字以外を除去) | "my-autumn-shoot" |
:dot |
ドットケース (dot.case) | "my.autumn.shoot" |
:abbrev |
各単語の頭文字 | "MAS" |
:trim |
先頭と末尾の空白を除去 | "My Autumn Shoot" |
例えば、workflow_name が "My Autumn Shoot" の場合:
{workflow_name:lower} → "my autumn shoot"
{workflow_name:upper} → "MY AUTUMN SHOOT"
{workflow_name:title} → "My Autumn Shoot"
{workflow_name:snake} → "my_autumn_shoot"
{workflow_name:pascal} → "MyAutumnShoot"
{workflow_name:camel} → "myAutumnShoot"
{workflow_name:screaming_snake} → "MY_AUTUMN_SHOOT"
{workflow_name:slug} → "my-autumn-shoot"
{workflow_name:dot} → "my.autumn.shoot"
{workflow_name:abbrev} → "MAS"
:snake、:pascal、:camel、:dot、および :screaming_snake は、大文字小文字の切り替わりで分割することにより、camelCase や PascalCase の入力も正しく処理するため、{varName:snake} → "var_name" も期待通りに機能します。
:trim は、他の変換を行う前の前処理ステップとして最も役立ちます。例えば、{name:trim:snake} は前後の空白を除去してからスネークケースへと変換します。
デフォルト値 (Default value)
{variable_name|default_value}
変数が提供されなかった場合、代わりに default_value が使用されます。
{workflow_name|untitled} → workflow_name が提供されない場合 "untitled" を使用
フォーマット指定の連結 (Chaining format specs)
複数のフォーマット指定は : で区切り、左から右へと順次適用されます。セパレータを使用する場合は、必ず先頭に配置する必要があります:
{variable_name:_:lower} → 末尾にアンダースコアが付加された小文字の値
{variable_name:lower:slug} → 小文字に変換後、スラッグ化
クォートされたセパレータ (Quoted separators)
セパレータ文字列が lower や upper などのキーワードと一致する場合は、単一引用符(シングルクォート)で囲むことでリテラルセパレータとして扱うことができます:
{variable_name:'lower'} → セパレータとしてテキスト "lower" を付加
解決 (Resolution)
マクロが解決される際、ディレクトリ名と組み込み変数はプロジェクトシステムによって自動的に供給されます。ユーザーは自身の操作に固有の変数(file_name_base や file_extension など)を提供するだけで済みます。
例えば、save_node_output シチュエーションマクロの解決:
テンプレート: {outputs}/{sub_dirs?:/}{node_name?:_}{file_name_base}{_index?:03}.{file_extension}
自動供給: outputs → "outputs" ディレクトリ定義から解決 → "outputs"
ユーザー提供: node_name="StyleTransfer", file_name_base="portrait", _index=3, file_extension="png"
結果: outputs/StyleTransfer_portrait003.png
ディレクトリ名(outputs など)は、設定されたパスへと自動的に解決されます。ディレクトリ を参照してください。
組み込み変数(workflow_name、project_dir など)も自動供給されます。環境と組み込み変数 を参照してください。
逆マッチング (Reverse matching)
マクロシステムは逆方向にも機能します:実際のパスとマクロテンプレートが与えられた場合、変数の値を抽出できます。これは、ファイルが既知のプロジェクトディレクトリに属しているかどうかや、ファイル名にどのようなメタデータがエンコードされているかをシステムが識別する必要がある場合に使用されます — 例えば、create_versioned_workflow の保存パスは、どの _index を繰り上げるかを判別するために既存ファイルの名前を再読み込みします。
公開 API は ParsedMacro.extract_variables(path, known_variables, secrets_manager)(または真偽値チェック用の matches(...))です。
基本的な例:
テンプレート: {outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension}
パス: outputs/StyleTransfer_portrait003.png
抽出結果: outputs="outputs", node_name="StyleTransfer", file_name_base="portrait", _index=3, file_extension="png"
抽出処理が各変数の終端を決定する方法
{a}/{b}/{c}.{ext} のようなテンプレートの場合、抽出処理は左から右へと走査します。各変数の値は次の アンカー (anchor) — パス内に必ず現れる固定文字列 — で終了します。アンカーには 2 種類あります:
- 次セグメントアンカー (Next-segment anchor): 後続の静的セグメントのテキスト(例:
.png)、または後続変数の先頭セパレータプレフィックス(例:{###?:^_v}内の_v)。 - 自己アンカー (Self-anchor): 変数自身の末尾セパレータ(例:
{node_name?:_}の_)。
抽出処理は、最も厳密で自己矛盾のない分割を生成するアンカーを採用します。両方が利用可能な場合、検索方向は後続の内容に依存します:次のセグメントが静的文字列の場合、静的文字列の前にある最も後ろ(LATEST)の自己アンカーを取得します(最も厳密な右端分割)。次のセグメントが別の変数の場合、最初(FIRST)の自己アンカーを取得します(後続の変数が自身の割り当て分を消費できる余地を残すため)。これによって、first_second_file.png に対する {a?:_}{b?:_}file.png は、a=first_second, b="" ではなく a=first, b=second に正しく分割されます。
オプショナル変数 (?) — 曖昧さの解決方法
オプショナル変数は、パスが書き込まれた時点で出力されていた場合と省略されていた場合があります。逆マッチングは、「各オプショナル変数が出力されたか省略されたか」の 全 2ᵏ 通りの組み合わせ(k はテンプレート内の未バインドなオプショナル変数の数)を列挙し、各組み合わせで抽出を行い、順方向のラウンドトリップを通じて検証します:抽出された値がテンプレートを通じて再解決され、その結果が入力パスとバイト単位で完全一致する必要があります。
組み合わせは popcount(1 のビット数)の降順 で試行されます — 最も情報量の多い解釈(最も多くのオプショナル変数が出力され、最も多くの情報が復元された状態)が、情報損失の大きい解釈よりも優先されます。ラウンドトリップに成功した最初の組み合わせが採用されます。
詳細な例 — 代表的なケース:
テンプレート: {workspace_dir}/{sub_dirs?:/}{file_name_base}{###?:^_v}.{file_extension}
既知の値: workspace_dir="/ws", file_extension="py"
パス: /ws/my_flow_v001.py
試行 1 (両方のオプショナルが出力されたと仮定): sub_dirs="my_flow", file_name_base="",
_index=1 → 解決結果: "/ws/my_flow/_v001.py" — 不一致 (MISS)
試行 2 (sub_dirs のみ出力): sub_dirs="my_flow_v001", file_name_base=""
→ 解決結果: "/ws/my_flow_v001/.py" — 不一致 (MISS)
試行 3 (_index のみ出力): file_name_base="my_flow", _index=1
→ 解決結果: "/ws/my_flow_v001.py" — 一致 (MATCH) ✓
3 回目の試行で一致したため、4 回目の組み合わせは試行されません。
知っておくべき留意事項:
- 空文字列をキャプチャした出力済みオプショナル変数は、ラウンドトリップが実行される前に拒否されます — 空の値をキャプチャすることは「スロットが出力された」という前提と矛盾するためです。
- ラウンドトリップ検証は貪欲な誤読を検出します:入力パスと異なる文字列に解決される抽出結果が採用されることは決してありません。抽出処理によるアンカーの貪欲な選択が安全であるのはこのためです — 誤った選択はラウンドトリップに失敗し、次の組み合わせが試行されます。
- 複数の組み合わせがラウンドトリップに成功した場合、最も popcount が高いものが採用されます。同じ popcount 内でのタイブレークはマスク値(昇順)によって決定されます。これは決定的ではありますが意味論的な優劣はありません — 後述の「曖昧さのないテンプレートの構築」を参照してください。
曖昧さのないテンプレートの構築
間に固定文字列を挟まない 2 つの変数は、両方が未バインドである場合、文法的に曖昧となります — 文法上、曖昧な境界のどちら側に文字が属するかを判定する手段がないためです。逆マッチングでも依然として 何らかの 有効な答えを返します(ラウンドトリップする解釈であればどれも正当です)が、どの解釈が選ばれるかはテンプレート間で予測できません。
設計上の指針:
- 確実な逆マッチングを行いたい場合は、隣接する変数間に 静的セパレータ(または明確な先頭セパレータプレフィックス)を配置してください。
{name}_{version}は明確ですが、{name}{version}は曖昧です。 - 先頭セパレータ付きのシーケンススロット形式 —
{###?:^_v}— は、バージョン管理されたファイル名に推奨されるパターンです。_vプレフィックスが特徴的なアンカーとなり、抽出処理はその前にある内容に関係なくバージョン境界を見つけることができます。 - 判明している値はあらかじめ
known_variablesで渡してください。既知の変数が 1 つ増えるごとに 2ᵏ の探索空間から 1 次元が削除され、曖昧さの要因が 1 つ排除されます。
逆パスでのフォーマット指定の挙動
すべてのフォーマット指定に可逆性があるわけではありません。指定を曖昧さなく元に戻すことができない場合、抽出処理は生の文字列を返し、呼び出し元に判断を委ねます。
| フォーマット指定 | 逆変換時の挙動 |
|---|---|
:03 (数値パディング) |
整数としてパースされます ("005" → 5)。非数値の値は指定に合致しないため、その抽出試行は失格となります。 |
{###} / {###?} (シーケンススロット) |
数値パディングと同様 — int にパースされます。バインドされると、値は _index として利用可能になります。 |
:_ (末尾セパレータ) |
末尾セパレータが存在する場合は除去します。存在しない場合は何もしません(べき等)。 |
:^_v (先頭セパレータ) |
プレフィックスが存在する場合は除去します。存在しない場合は何もしません(べき等)。先行する変数の抽出が後続する場合、プレフィックスはアンカーとしても機能します。 |
:lower / :upper / :title / … |
ケース変換は抽出された部分文字列をそのまま返します;元の大文字小文字の区別を復元することはできません。ラウンドトリップの忠実性が必要な呼び出し元は、マッチキーでのこれらの使用を避けるべきです。 |
:slug / :snake / :pascal / … |
同様です — 一方通行の変換は生の抽出値を返します。 |
\|default_value |
デフォルト値は順方向専用の構造です。逆パスでは変数がパスから抽出されるか未バインドのままとなり、デフォルトテキストが再注入されることは決してありません。 |
実用上の制限
- 逆マッチングでは、未バインドなオプショナル変数の数は最大 5 個に制限されています(2⁵ = 32 通りの組み合わせ)。6 個以上の未バインドなオプショナル変数を持つテンプレートは
MacroParseFailureReason.TOO_MANY_OPTIONAL_VARIABLESを発生させます。コードベース内の実際のテンプレートは最大でも 3 個程度です;この制限は異常な文法による計算の暴走を防ぐために存在します。 - この制限は、
known_variablesによってバインドされていないオプショナル変数のみをカウントします。オプショナル変数を事前バインドすると探索空間から除外されるため、6 個のオプショナル変数を持つテンプレートであっても、呼び出し元からすべて渡されていれば逆マッチングが可能です。 - パスマッチングは バイト完全一致 です。プラットフォーム間を跨ぐ呼び出し元は、
extract_variablesを呼び出す前にパスセパレータをスラッシュ(/)に正規化する必要があります — 組み込みのプロジェクトディレクトリマッチハンドラは、自動解決されたディレクトリ組み込み変数に対してこれを自動的に行います。
構文エラー (Syntax errors)
マクロパーサーは、問題箇所を特定しやすいように位置番号付きで構文エラーを報告します:
- 閉じられていない中括弧:
{variable_name(}がない) - 対応する開始括弧のない閉じ中括弧:
variable}name - ネストされた中括弧:
{outer{inner}} - 空の変数:
{}