プリンターやディスクなど Windows 固有の情報は、PowerShell に任せると短く書けます。Tauri からは shell プラグインで powershell.exe(標準で入っている Windows PowerShell 5.1)を起動し、1 行で済む処理は -Command、スクリプトは -File で実行します。つまずきやすいのは実行ポリシー、引数の渡し方、文字コードの 3 点です。macOS / Linux 向けは Bash スクリプトを実行する を参照してください。
前提条件
shell プラグインを追加します。
npm run tauri add shell
フロントエンドから実行する内容は shell:allow-execute に登録します。powershell を "args": true で許可すると任意のコマンドを実行できてしまうので、引数はすべて固定しています(書き方は 外部コマンド(ls/dir等)を実行する)。
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"shell:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "ps-printers",
"cmd": "powershell",
"args": [
"-NoProfile",
"-NonInteractive",
"-Command",
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ConvertTo-Json -Compress -InputObject @(Get-Printer | Select-Object Name, DriverName)"
]
}
]
}
]
}
| オプション | 意味 |
|---|---|
-NoProfile | ユーザーのプロファイルを読まない(PC ごとの設定に左右されない) |
-NonInteractive | 入力待ち(Read-Host や確認)で止まらず、エラーにする |
-ExecutionPolicy Bypass | この起動に限り実行ポリシーを無視する |
-File <パス> <引数> | スクリプトを実行し、後ろの引数を param() に文字列として渡す |
-Command <文字列> | 文字列を PowerShell のコードとして実行する |
-File と -Command は最後に置きます。それより後ろはすべてスクリプトやコマンドの一部として扱われます。
1. フロントエンドから実装する (TypeScript)
1 行のコマンドの結果を JSON で受け取る
結果は ConvertTo-Json で JSON にすると、表示用の整形に左右されずに扱えます。要素が 1 つだけだと配列でなくオブジェクトが出るので、-InputObject @(...) で常に配列にしています。実行ポリシーは .ps1 ファイルが対象で、-Command のコマンドには効きません。
import { Command } from '@tauri-apps/plugin-shell';
type Printer = { Name: string; DriverName: string };
export async function listPrinters(): Promise<Printer[]> {
// 引数はすべて capability 側で固定しているので、ここでは渡さない
const out = await Command.create('ps-printers').execute();
if (out.code !== 0) throw new Error(out.stderr.trim() || `exit code ${out.code}`);
return JSON.parse(out.stdout) as Printer[];
}
日本語が読めないとき
日本語版の Windows では、PowerShell(5.1 も 7 も)がパイプに書き出す文字は Shift_JIS です。shell プラグインは既定で UTF-8 として読むので、日本語が混じると execute() が「invalid utf-8 sequence of 1 bytes from index 0」のようなエラーで失敗します。先頭で [Console]::OutputEncoding を UTF-8 にすれば、以降の出力は UTF-8 になります。コマンドを変えられないなら、Command.create('ps-printers', [], { encoding: 'shift_jis' }) のように JS 側で読み方を合わせます。
2. バックエンドから実装する (Rust)
同梱したスクリプトを -File で実行する
利用者の入力を渡すスクリプトは -File で実行します。-File の後ろの引数は param() に文字列として渡り、$(...) や ; を含んでもコードとしては実行されません。空白や日本語を含む値もそのまま届きます。-Command は残りの引数をつないでコードとして実行するので、入力を混ぜると任意のコマンドを実行されるおそれがあります。
スクリプトは src-tauri/scripts/ に置き、リソースとして同梱します(画像やDBファイルを配布物に同梱する)。
{
"bundle": {
"resources": ["scripts/disk-report.ps1"]
}
}
スクリプトは UTF-8(BOM 付き) で保存します。Windows PowerShell 5.1 は BOM の無いファイルを Shift_JIS として読むため、日本語の文字列が「縺薙s縺ォ縺。縺ッ」のように化けます。
param(
[Parameter(Mandatory = $true)][string]$Drive
)
$ErrorActionPreference = 'Stop' # エラーでスクリプトを止め、終了コードを 1 にする
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 # ここから後の出力を UTF-8 にする
$d = Get-PSDrive -Name $Drive
[pscustomobject]@{
Drive = $Drive
FreeGB = [math]::Round($d.Free / 1GB, 1)
UsedGB = [math]::Round($d.Used / 1GB, 1)
} | ConvertTo-Json -Compress
Rust からもプラグインの app.shell().command() で起動します。capability の範囲に縛られないので、受け取った値はここで確かめます。スクリプトが動き出す前のエラー(実行ポリシーや必須パラメーターの不足)は Shift_JIS で出るので、UTF-8 として読めなければ Shift_JIS として読み直します。
use tauri::path::BaseDirectory;
use tauri::Manager;
use tauri_plugin_shell::process::Encoding;
use tauri_plugin_shell::ShellExt;
/// UTF-8 として読めなければ Shift_JIS として読む
fn decode_console(bytes: &[u8]) -> String {
let text = match std::str::from_utf8(bytes) {
Ok(s) => s.to_string(),
Err(_) => match Encoding::for_label(b"shift_jis") {
Some(enc) => enc.decode(bytes).0.into_owned(),
None => String::from_utf8_lossy(bytes).into_owned(),
},
};
text.trim().to_string()
}
#[tauri::command]
async fn disk_report(app: tauri::AppHandle, drive: String) -> Result<String, String> {
if drive.len() != 1 || !drive.chars().all(|c| c.is_ascii_alphabetic()) {
return Err("drive must be a single letter".into());
}
let script = app
.path()
.resolve("scripts/disk-report.ps1", BaseDirectory::Resource)
.map_err(|e| e.to_string())?;
let output = app
.shell()
.command("powershell")
.args(["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File"])
.arg(&script)
.args(["-Drive", drive.as_str()]) // ここから後ろはスクリプトの param() に渡る
.output()
.await
.map_err(|e| e.to_string())?;
if output.status.success() {
Ok(decode_console(&output.stdout))
} else {
Err(decode_console(&output.stderr))
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![disk_report])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
import { invoke } from '@tauri-apps/api/core';
type DiskReport = { Drive: string; FreeGB: number; UsedGB: number };
const report = JSON.parse(await invoke<string>('disk_report', { drive: 'C' })) as DiskReport;
console.log(`${report.Drive}: 空き ${report.FreeGB} GB / 使用 ${report.UsedGB} GB`);
作業ディレクトリはアプリと同じなので、スクリプトと同じフォルダーのファイルは $PSScriptRoot を基準に開きます(作業ディレクトリの指定は コマンドに引数・作業ディレクトリ・環境変数を渡す)。
実行ポリシー
Windows のクライアント版の既定の実行ポリシーは Restricted で、.ps1 は実行できません。-ExecutionPolicy Bypass はその起動にだけ効き、管理者権限は要らず、PC の設定も変えないので、利用者に Set-ExecutionPolicy を頼む必要はありません。ただし会社の PC などでグループポリシーが実行ポリシーを決めている場合はそちらが優先され、この指定は効きません。Get-ExecutionPolicy -List で MachinePolicy か UserPolicy が Undefined 以外なら、この状態です。
動作確認
npm run tauri dev で起動して listPrinters() を呼ぶと、プリンターが 1 台でも配列で返ります。disk_report を 'C' で呼ぶと次のように出ます。
C: 空き 601.3 GB / 使用 329.1 GB
存在しないドライブ 'Q' を渡すと、invoke() が「Get-PSDrive : ドライブが見つかりません。名前 'Q' のドライブが存在しません。」で始まるエラーで失敗します。
よくあるエラーと対処法
- 「invalid utf-8 sequence of 1 bytes from index …」: 出力が Shift_JIS です。
[Console]::OutputEncodingを UTF-8 にするか、encoding: 'shift_jis'を指定します。 - 「このシステムではスクリプトの実行が無効になっているため、ファイル … を読み込むことができません。」: 実行ポリシーで止まっています。
-ExecutionPolicy Bypassを付けます。この文面も Shift_JIS で出るので、JS から既定の設定で実行すると上の UTF-8 のエラーに化けて見えます。 - 「program not allowed on the configured shell scope: ps-printers」:
allowのnameとCommand.create()の名前が違うか、shell:allow-spawn側にだけ登録してexecute()を呼んでいます。 - スクリプトが失敗したのに終了コードが 0:
Write-Errorはスクリプトを止めません。$ErrorActionPreference = 'Stop'にするか、失敗したらexit 1で終えます。
OS ごとの違いと注意点
- Windows:
powershell.exe(5.1)は Windows 10 / 11 に標準で入っています。PowerShell 7 のpwshは別途インストールした PC にしか無いので、配布するアプリではpowershellが確実です。pwshは BOM の無い UTF-8 のスクリプトも正しく読みますが、出力が Shift_JIS なのは同じです。 - Windows: shell プラグイン(JS と
app.shell())で起動するとコンソールウィンドウは開かないので、-WindowStyle Hiddenは要りません。 - macOS / Linux:
powershellは無く、pwshも標準では入っていないので、Bash スクリプトを実行する の方法を使います。 - 共通: 時間のかかるスクリプトの進み具合は、
spawn()で 1 行ずつ受け取ります(コマンドの出力をリアルタイムで受け取る)。
