myapp --verbose で詳しいログを出す、myapp memo.txt や「プログラムから開く」で渡されたファイルを開く、といった動きには起動時の引数が要ります。取り方は 2 つです。CLI プラグインは tauri.conf.json に定義した引数を解析して JS の getMatches() と Rust の app.cli().matches() に渡し、--help も自動で用意します。Rust 標準の std::env::args_os() は、定義なしで生の引数を読めます。使い分けと、開発中の渡し方、二重起動時の引数の扱いも説明します。
前提条件
npm run tauri add cli
CLI プラグインはデスクトップ専用です。tauri add cli はクレートをデスクトップ向けだけに追加し、権限 cli:default(getMatches() を許可する cli:allow-cli-matches を含む)を capabilities/desktop.json に書き込みます。core:default には含まれないので、手で書く場合は次の形にします。
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"platforms": ["macOS", "windows", "linux"],
"windows": ["main"],
"permissions": [
"cli:default"
]
}
受け付ける引数は tauri.conf.json の plugins.cli に定義します。tauri add はここまでは書かないので、自分で足します。
{
"plugins": {
"cli": {
"description": "My App",
"args": [
{ "name": "verbose", "short": "v", "description": "詳しいログを出す" },
{ "name": "lang", "takesValue": true, "possibleValues": ["ja", "en"], "description": "表示言語" },
{ "name": "file", "index": 1, "takesValue": true, "description": "開くファイル" }
]
}
}
}
index の無い引数は --lang ja のように名前で、index を付けた引数は位置で受け取ります。-h / --help と -V / --version は自動で追加されるので、自分の引数の short には使えません。
2 つの方法は次のように使い分けます。
| やりたいこと | 使うもの |
|---|---|
--verbose や --lang ja のようなオプションを受ける | CLI プラグイン |
| ファイルの関連付けなどで渡されたパスを受け取るだけ | std::env::args_os() |
| 知らない引数が来てもエラーにしない | std::env::args_os() |
| 起動中にもう一度起動されたときの引数 | Single Instance プラグインのコールバック |
1. フロントエンドから実装する (TypeScript)
getMatches() は起動時の引数を定義に沿って解析した結果を返します。定義した引数は渡されなくてもキーがあり、フラグは false、値を取る引数は null です。value の型は string | boolean | string[] | null なので、型を確かめてから使います。
import { getMatches } from '@tauri-apps/plugin-cli';
export type LaunchOptions = { verbose: boolean; lang: string | null; file: string | null };
export async function readLaunchOptions(): Promise<LaunchOptions> {
const { args } = await getMatches(); // cli:default が必要
const str = (v: unknown) => (typeof v === 'string' ? v : null);
return {
verbose: args.verbose?.value === true,
lang: str(args.lang?.value), // "ja" / "en" 以外は解析の段階でエラーになる
file: str(args.file?.value),
};
}
--help を付けて起動すると、定義した引数の代わりに args.help.value にヘルプの文面が入ります。アプリは普段どおり起動するので、ページなどに自分で表示します。
import { getMatches } from '@tauri-apps/plugin-cli';
const { args } = await getMatches();
const help = args.help?.value;
if (typeof help === 'string') {
const pre = document.createElement('pre');
pre.textContent = help; // 使い方と引数の一覧
document.body.replaceChildren(pre);
}
2. バックエンドから実装する (Rust)
Rust では tauri_plugin_cli::CliExt を use すると、app.cli().matches() で同じ結果を受け取れます。ウィンドウの表示前に決めたい設定(ログの詳しさなど)は、setup で読みます(アプリ起動・終了時の処理を書く)。
生の引数を見るだけなら std::env::args_os() で足ります。先頭は実行ファイル自身のパスなので飛ばします。std::env::args() は Unicode として不正な引数が 1 つでもあるとパニックするので、ファイルパスを扱うなら args_os() を使います。
getMatches() と matches() が解析するのは、今のプロセスの引数だけです。起動中にもう一度起動されたとき(エクスプローラーで別のファイルを開いたときなど)の引数は Single Instance プラグイン(npm run tauri add single-instance)のコールバックに届くので、matches_from() で同じ定義に沿って解析してページへ送ります。このプラグインは最初に登録し、相対パスはコールバックの cwd(2 回目の起動時の作業フォルダー)を基準に解決します。
use std::path::PathBuf;
use serde::Serialize;
use tauri::{Emitter, Manager};
use tauri_plugin_cli::{CliExt, Matches};
#[derive(Clone, Serialize)]
struct SecondLaunch {
matches: Matches,
cwd: String,
}
/// 生の引数から、存在するファイルのパスを 1 つ探す(CLI プラグインを使わない方法)
#[tauri::command]
fn file_from_args() -> Option<String> {
std::env::args_os()
.skip(1) // 先頭は実行ファイル自身のパス
.map(PathBuf::from)
.find(|p| p.is_file())
.map(|p| p.to_string_lossy().into_owned())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
// 起動中にもう一度起動されると、その引数がここに届く(最初に登録する)
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
if let Ok(matches) = app.cli().matches_from(argv) {
let _ = app.emit("second-launch", SecondLaunch { matches, cwd });
}
if let Some(window) = app.get_webview_window("main") {
let _ = window.set_focus();
}
}))
.plugin(tauri_plugin_cli::init())
.setup(|app| {
match app.cli().matches() {
Ok(matches) => {
let verbose = matches
.args
.get("verbose")
.and_then(|a| a.value.as_bool())
.unwrap_or(false);
if verbose {
println!("verbose モードで起動しました");
}
}
// 定義に無い引数などで解析に失敗しても、アプリ自体は起動させる
Err(e) => eprintln!("{e}"),
}
Ok(())
})
.invoke_handler(tauri::generate_handler![file_from_args])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import type { CliMatches } from '@tauri-apps/plugin-cli';
// 起動時に渡されたファイル(CLI プラグインの定義に関係なく探す)
const path = await invoke<string | null>('file_from_args');
if (path) console.log('起動時のファイル:', path);
// 起動中にもう一度起動されたとき
await listen<{ matches: CliMatches; cwd: string }>('second-launch', ({ payload }) => {
const file = payload.matches.args.file?.value;
if (typeof file === 'string') console.log('2 回目の起動のファイル:', file, '作業フォルダー:', payload.cwd);
});
動作確認
開発中は tauri dev の後ろに -- を 2 回書き、2 回目の後ろに書いたものがアプリに渡ります(1 回目との間は Rust のビルドへの引数)。npm run 経由では npm が最初の -- を 1 つ消費するので、3 回書きます。
npm run tauri dev -- -- -- --verbose --lang ja ./memo.txt
ターミナルに「verbose モードで起動しました」と出て、readLaunchOptions() は { verbose: true, lang: "ja", file: "./memo.txt" } を返します。--lang fr にすると「failed to parse arguments:」で始まるエラーになり、--help にすると 2 つ目のコードでページにヘルプが表示されます。tauri dev で動くアプリの作業フォルダーは src-tauri なので、相対パスの ./memo.txt は src-tauri/memo.txt を指します。
よくあるエラーと対処法
- 起動直後に「Error deserializing 'plugins.cli' within your Tauri configuration」を含むエラーで止まる:
tauri.conf.jsonにplugins.cliがありません。引数を定義しない場合も"plugins": { "cli": {} }は必要です。 - 「failed to parse arguments:」で始まるエラー: 定義に無い引数(
unexpected argument '--foo' foundなど)や、possibleValuesに無い値が渡されました。ファイルの関連付けで起動されるアプリは、index付きの引数を定義しておかないと、ファイルのパスも「定義に無い引数」になります。 - 「Short option names must be unique for each argument」を含むパニック:
shortにhやVを使い、自動で追加される--help/--versionとぶつかっています。開発ビルドで引数を解析したときに起きます。 - 「cli.cli_matches not allowed. Permissions associated with this command: cli:allow-cli-matches, cli:default」: どの capability にも
cli:defaultがありません。desktop.jsonを確かめます。サブウィンドウからだけ「cli.cli_matches not allowed on window」で始まるエラーになる場合は、そのラベルをwindowsに足します。リリースビルドではどちらも「Command plugin:cli|cli_matches not allowed by ACL」になります。
OS ごとの違いと注意点
- Windows: リリースビルドはコンソールを持たない設定(
main.rsのwindows_subsystem = "windows")なので、ターミナルから起動してもprintln!の出力は表示されません。ヘルプやエラーはページかダイアログに出します。 - macOS: ファイルの関連付けで開いたパスは、Windows と Linux では引数で届きますが、macOS では Rust の
RunEvent::Openedで届きます。.appに引数を渡して試すなら、open -a MyApp --args --verboseかMyApp.app/Contents/MacOS/MyApp --verboseで起動します。 - iOS / Android: CLI プラグインは使えません。
- 共通: 引数はタスクマネージャーや
psで他のプロセスからも見えるので、パスワードやトークンは渡しません。起動時の設定は 環境変数を取得・設定する の方法でも渡せます。受け取った--langを表示言語に反映する方法は システムの言語設定(ロケール)を取得する を参照してください。
