コマンドの実行結果(標準出力)を取得する方法を解説します。
await cmd.execute() を使う方法と、イベントリスナーを使ってリアルタイムに取得する方法があります。
前提条件
プラグインのインストールが必要です。
npm run tauri add shell
Permissions (権限) の設定
src-tauri/capabilities/default.json に以下の権限を追加します。
{
"permissions": [
...,
"shell:allow-open",
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "echo",
"cmd": "echo",
"args": true
},
{
"name": "ping",
"cmd": "ping",
"args": true
}
]
},
{
"identifier": "shell:allow-spawn",
"allow": [
{
"name": "ping",
"cmd": "ping",
"args": true
}
]
}
]
}
1. フロントエンドから作成する (TypeScript)
1. execute() (非同期・完了待機)
コマンドが終了するまで待機し、全ての結果をまとめて取得します。短時間のコマンドに適しています。
2. on('close', ...) + on('error', ...) (イベント駆動)
cmd.spawn() を使用し、イベントリスナーで出力を受け取ります。長時間実行されるコマンドや、プログレスバーを表示したい場合に適しています。
一括取得 (execute)
import { Command } from '@tauri-apps/plugin-shell';
async function getOutput() {
const cmd = Command.create('echo', ['output data']);
const output = await cmd.execute();
// string として取得
console.log(`STDOUT: ${output.stdout}`);
}
リアルタイム取得 (spawn)
import { Command } from '@tauri-apps/plugin-shell';
async function streamOutput() {
// ping -c で回数を指定
const cmd = Command.create('ping', ['127.0.0.1', '-c', '5']);
cmd.on('close', data => {
console.log(`command finished with code ${data.code} and signal ${data.signal}`);
});
cmd.on('error', error => console.error(`command error: "${error}"`));
// 標準出力のイベント
cmd.stdout.on('data', line => {
console.log(`[STDOUT] ${line}`);
});
// エラー出力のイベント
cmd.stderr.on('data', line => {
console.log(`[STDERR] ${line}`);
});
const child = await cmd.spawn();
console.log('pid:', child.pid);
}
2. バックエンドから作成する (Rust)
Rust で標準出力を取得するには Command::output() を使用します。
リアルタイムにストリーミングしたい場合は、Stdio::piped() を使用して子プロセスを起動し、別スレッドで読み取ります。
一括取得
use std::process::Command;
#[tauri::command]
fn get_output() -> String {
let output = Command::new("echo")
.arg("output data")
.output()
.expect("failed to execute process");
String::from_utf8_lossy(&output.stdout).to_string()
}
リアルタイム取得 (イベント通知)
use tauri::{AppHandle, Emitter};
use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader};
#[tauri::command]
fn stream_output(app: AppHandle) {
let mut child = Command::new("ping")
.args(["127.0.0.1", "-c", "5"])
.stdout(Stdio::piped())
.spawn()
.expect("failed to spawn");
let stdout = child.stdout.take().unwrap();
let reader = BufReader::new(stdout);
// 別スレッドで読み取り、イベントを発行
std::thread::spawn(move || {
for line in reader.lines() {
if let Ok(l) = line {
// フロントエンドへ送信
let _ = app.emit("process-stdout", l);
}
}
});
}
コマンドの登録 (lib.rs)
作成したコマンドを invoke_handler に登録します。
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![get_output, stream_output])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
フロントエンドでの受信 (TypeScript)
バックエンドから送信されたイベント (process-stdout) をリッスンします。
import { listen } from '@tauri-apps/api/event';
import { invoke } from '@tauri-apps/api/core';
// イベントリスナーの登録
const unlisten = await listen<string>('process-stdout', (event) => {
console.log(`Received: ${event.payload}`);
});
// コマンドの呼び出し
invoke('stream_output');
// 必要なタイミングで解除
// unlisten();