注目イベント!
アドベントカレンダー2024開催中!
一年を締めくくる特別なイベント、アドベントカレンダーを今年も開催しています!
初心者からベテランまで楽しめる内容で、毎日新しい技術トピックをお届けします。
詳細はこちらから!
event banner

Deno 1.31で安定化されたプロセス起動 API Deno.Command を使ってみる

| 3 min read
Author: masahiro-kondo masahiro-kondoの画像

Deno にはサブプロセスを起動する API がいくつかあります。現在公式マニュアルに記載されているのは Deno.run です。

Creating a Subprocess | Manual | Deno

Deno 1.28 で Deno.Command が不安定版として追加され、1.31 で安定化されました。

Deno.run

#

以前執筆した「Deno を始める」連載の第4回では、Deno.run API を紹介していました。

Deno を始める - 第4回 (OS 機能と FFI の利用)

cat コマンドにファイルのパスを渡して起動し結果を出力するサンプルです。

const fileNames = Deno.args;

const p = Deno.run({
  cmd: [
    "cat",
    ...fileNames,
  ],
  stdout: "piped",
  stderr: "piped"
});

const { code } = await p.status();
const rawOutput = await p.output();
const rawError = await p.stderrOutput();

if (code === 0) {
  console.info(new TextDecoder().decode(rawOutput));
} else {
  console.error(new TextDecoder().decode(rawError));
}

Deno.exit(code);

Deno.run のオプションで、標準出力、標準エラー出力をパイプで受け取るようにしています。Deno.run はサブプロセスのオブジェクトを返却するので、そのプロパティから結果コード・標準出力・標準エラー出力を取得しています。標準出力および標準エラー出力は、Uint8Array なので、TextDecoder を使ってデコードしています。

この Deno.run API は Deno.Command の安定化に伴い、非推奨化が決まっているようです。

Deno.Command

#

Deno.Command の API ドキュメントは以下です。

Deno.Command | Runtime APIs | Deno

Deno.Command オブジェクトを作成して、outputspawn メソッドを呼ぶという2フェーズの API になっています。

output メソッドは、非同期呼び出しで、Promise<CommandOutput> を返します[1]

以下は Deno.run のサンプルと同様、cat コマンドにファイルパスを渡して結果を出力するものです。output メソッドから結果コード・標準出力・標準エラー出力を取得し、結果コードに応じて読み取ったファイルの内容かエラーメッセージを出力しています。

const fileNames : string[] = Deno.args;

const command = new Deno.Command(
  "cat", {
  args: fileNames
});

const { code, stdout, stderr } = await command.output();

if (code === 0) {
  console.info(new TextDecoder().decode(stdout));
} else {
  console.error(new TextDecoder().decode(stderr));
}

このスクリプトを実行するには、--allow-run オプションを指定します。

deno run --allow-run main.ts hoge.txt

起動した子プロセスの標準入力にパイプ渡しする例も紹介します。spawn メソッドを使用しています。

const fileNames : string[] = Deno.args;

const p = new Deno.Command("cat", {
  stdin: "piped",
  stdout: "piped",
}).spawn();

const file = await Deno.open(fileNames[0]);
file.readable.pipeTo(p.stdin);

const { stdout } = await p.output();

console.info(new TextDecoder().decode(stdout));

わざとらしいですが、ファイルのパスを cat の引数として渡すのではなく、Deno.open でファイルを開いて spawn で起動したプロセスの標準入力にパイプ渡ししています。このスクリプトを実行するには、--allow-run に加え、内部的なファイル読み取り用の --allow-read オプションも必要です。

deno run --allow-run --allow-read main.ts hoge.txt

最後に

#

以上、Deno.Command API の利用方法を紹介しました。Deno.run では、起動するコマンドを配列の形で渡していましたが、Deno.Command では専用のオブジェクトにより扱いやすくなっています。普通に実行するだけなら spawn メソッドではなく output を使うとパイプの扱いが不要になり、コードがシンプルになります。

Deno.Command 登場前に Deno.spawn という、これまた子プロセスを起動する API があったのですが、すでに削除されています。基本的な API が短期間で変わっていくのは開発が活発な証拠ですし、API の洗練だけでなくパフォーマンス改善もされているのだとは思いますが、バージョンアップ時に多少の痛みを伴うという側面はあります。


  1. 同期呼び出し用の outputSync メソッドもあります。 ↩︎

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。