CLIアプリケーション
ターミナルに出力するコマンドラインプロセスを構築し、入力、色、システム情報、名前付きコマンドへと拡張します。
分類: 実行可能なチュートリアルです。挨拶アプリケーションは完全な構成です。
後半の各セクションは、記載されているとおりsrc/cli.luaまたはapp:cliエントリを
置き換えて試せるオプションです。
構築するもの
挨拶を表示するCLIプロセス:
$ wippy run -x app:cli
Hello from CLI!
前提条件
wippyとして実行できるWippyランタイムv0.3.32a。wippy version --shortで確認してください。- 対話型ターミナル。入力例には標準入力が必要で、カラー表示にはANSIエスケープシーケンスを 表示できるターミナルが必要です。
プロジェクト構造
cli-app/
├── wippy.lock
└── src/
├── _index.yaml
└── cli.lua
ステップ1: プロジェクトの作成
mkdir cli-app && cd cli-app
mkdir src
ステップ2: エントリ定義
src/_index.yamlを作成:
version: "1.0"
namespace: app
entries:
# Terminal host connects processes to stdin/stdout
- name: terminal
kind: terminal.host
lifecycle:
auto_start: true
# CLI process
- name: cli
kind: process.lua
source: file://cli.lua
method: main
modules:
- io
terminal.hostはLuaプロセスをターミナルに接続します。これがないと、io.print()の出力先がありません。
ステップ3: CLIコード
src/cli.luaを作成:
local io = require("io")
local function main()
io.print("Hello from CLI!")
return 0
end
return { main = main }
ステップ4: 実行
wippy init
wippy run -x app:cli
期待される出力:
Hello from CLI!
-xフラグはプロセスをコマンドとして実行します。レジストリ内に1つだけある
terminal.hostを自動検出します。複数のターミナルホストがある場合は--hostを
使用してください。ログフラグを指定しないコマンドモードではランタイムログが抑制され、
プロセスの出力を読みやすく保ちます。
ユーザー入力の読み取り
src/cli.luaを次のバージョンに置き換えます。ターミナルの読み書きエラーを空入力として扱わず報告します:
local io = require("io")
local function main()
local _, write_err = io.write("Enter your name: ")
if write_err then
io.eprint("Cannot write prompt:", write_err)
return 1
end
local _, flush_err = io.flush()
if flush_err then
io.eprint("Cannot flush prompt:", flush_err)
return 1
end
local name, read_err = io.readline()
if read_err then
io.eprint("Cannot read input:", read_err)
return 1
end
if name and #name > 0 then
io.print("Hello, " .. name .. "!")
else
io.print("Hello, stranger!")
end
return 0
end
return { main = main }
カラー出力
ANSIエスケープコードで色を付けるには、src/cli.luaを次のバージョンに置き換えます:
local io = require("io")
local reset = "\027"
local function red(s) return "\027" .. s .. reset end
local function green(s) return "\027" .. s .. reset end
local function yellow(s) return "\027" .. s .. reset end
local function cyan(s) return "\027" .. s .. reset end
local function bold(s) return "\027" .. s .. reset end
local function main()
io.print(bold(cyan("Welcome!")))
local _, write_err = io.write(yellow("Enter a number: "))
if write_err then
io.eprint("Cannot write prompt:", write_err)
return 1
end
local _, flush_err = io.flush()
if flush_err then
io.eprint("Cannot flush prompt:", flush_err)
return 1
end
local input, read_err = io.readline()
if read_err then
io.eprint("Cannot read input:", read_err)
return 1
end
local n = tonumber(input)
if n then
io.print("Squared: " .. green(tostring(n * n)))
return 0
else
io.print(red("Error: ") .. "not a number")
return 1
end
end
return { main = main }
システム情報
systemモジュールでランタイム統計にアクセスします。すべての読み取りはsystem.readアクションでガードされているため、プロセスにはそれを許可するポリシーも必要です:
# エントリに追加
- name: system_read
kind: security.policy
policy:
actions:
- system.read
resources: "*"
effect: allow
# cliエントリを更新
- name: cli
kind: process.lua
source: file://cli.lua
method: main
security:
policies:
- app:system_read
modules:
- io
- system
続いてsrc/cli.luaを置き換えます:
local io = require("io")
local system = require("system")
local function main()
local hostname, hostname_err = system.process.hostname()
if hostname_err then
io.eprint("Cannot read hostname:", hostname_err)
return 1
end
local cpu_count, cpu_err = system.runtime.cpu_count()
if cpu_err then
io.eprint("Cannot read CPU count:", cpu_err)
return 1
end
local goroutines, goroutine_err = system.runtime.goroutines()
if goroutine_err then
io.eprint("Cannot read goroutine count:", goroutine_err)
return 1
end
local mem, memory_err = system.memory.stats()
if memory_err then
io.eprint("Cannot read memory stats:", memory_err)
return 1
end
io.print("Host: " .. hostname)
io.print("CPUs: " .. cpu_count)
io.print("Goroutines: " .. goroutines)
io.print("Memory: " .. string.format("%.1f MB", mem.heap_alloc / 1024 / 1024))
return 0
end
return { main = main }
名前付きコマンド
-x app:cliを使わずに名前でプロセスを呼び出すには、コマンドメタデータを追加します。
app:cliエントリを次のバージョンに置き換えます。基本プロジェクトのterminal.hostエントリは残してください。
- name: cli
kind: process.lua
meta:
command:
name: greet
short: Greet the user
source: file://cli.lua
method: main
modules:
- io
名前付きコマンドを実行します:
wippy run greet
利用可能なすべてのコマンドを一覧表示:
wippy run list
Available commands:
greet Greet the user (app:cli)
Run with: wippy run <command>
終了コード
main()から数値を返すとプロセスの終了コードを設定できます:
local function main()
if error_occurred then
return 1 -- Error
end
return 0 -- Success
end
I/Oリファレンス
| 関数 | 戻り値 | 説明 |
|---|---|---|
io.print(...) |
ターミナルコンテキストがない場合はbooleanまたはnil, error |
タブ区切りと末尾の改行を付けてstdoutに書き込み |
io.write(...) |
boolean, error |
区切り文字や改行を付けずにstdoutへ書き込み |
io.eprint(...) |
ターミナルコンテキストがない場合はbooleanまたはnil, error |
タブ区切りと末尾の改行を付けてstderrに書き込み |
io.readline() |
string, error |
末尾の改行を除いて1行読み取り。データのないEOFはエラー |
io.flush() |
boolean, error |
ストリームが対応している場合にstdoutをフラッシュ |
CLIフラグ
| フラグ | 説明 |
|---|---|
wippy run -x app:cli |
CLIプロセスを実行(terminal.hostを自動検出) |
wippy run -x app:cli --host app:terminal |
明示的なターミナルホスト |
wippy run -x app:cli -v |
詳細ログ付き |
トラブルシューティングとクリーンアップ
no terminal host foundはレジストリにterminal.hostがないことを示します。ステップ2のエントリを使用してください。 複数のホストがある場合は--host app:terminalを渡します。no terminal contextはプロセスがターミナルホスト経由で起動されていないことを示します。 バックグラウンドのprocess.serviceではなく、wippy run -x app:cliを使用してください。- 標準入力が閉じている場合、EOFでの入力エラーは想定どおりです。入力例は対話型ターミナルで実行してください。
- ANSIシーケンスが文字列として表示される場合は、カラーなしの例かANSI対応ターミナルを使用してください。
main()が戻るとコマンドは終了します。使い捨ての演習であれば、ディレクトリを離れた後にcli-app/を削除してください。
次のステップ
- I/Oモジュール — I/O APIリファレンス
- Systemモジュール — ランタイムとシステム情報
- Echoサービス — マルチプロセスアプリケーションを構築する