6
1

More than 1 year has passed since last update.

`Deno.run`は非推奨になるので代替手段(`Deno.Command`、dax)

Posted at

Deno.runが非推奨になった

Deno v1.31以降、Deno.runに代わる新しいAPI、Deno.Commandが登場しています。
今後はDeno.Commandを使うのが推奨されると共に、Deno.runは非推奨化&削除予定です。

時系列

2018年11月 Deno.run()が導入される #1156
2022年4月 Deno.run()の代替としてDeno.spawn()Deno.spawnChild()が導入される(実験的API扱い) #11618
2022年11月 Deno.spawn()Deno.spawnChild()の代替として、Deno.Command()が導入される(実験的API扱い) #16516
2022年12月 Deno.spawn()Deno.spawnChild()が削除される #16893
2023年2月 Deno.Command()が安定化する #17628
現在 Deno.run()は非推奨化予定、Deno.Command()の使用を推奨

…と若干迷走している感じもあったサブプロセスAPIの改修ですが、最終的に導入されたDeno.Command()はRustライクなAPIになっており、これで確定する可能性が高いです。

なお、ここまでの経緯はDenoコアチームのkt3kさんの記事が詳しいです。

非推奨化されるDeno.runの代替手段としては、主に2つの方法がありますので、紹介したいと思います。

方法1:Deno.Command APIを使う

上で紹介しているように、Deno.runの後継はDeno.Command APIになります。
試しにDeno.Commandを使ってサブプロセスでdeno evalコマンドを起動してみたいと思います。

const decoder = new TextDecoder();

// 第1引数にはコマンド名を文字列で指定
const command = new Deno.Command(Deno.execPath(), {
  args: ["eval", "console.log('hello'); console.error('hello from stderr');"],
  /* 子プロセスを動かすカレントディレクトリを指定 */
    // cwd: "path/to/cwd",
  /* stdin / stdout / stderrをどこから渡すかを制御する */
    // stdin: "inherit",
  /* stdout: "inherit",
    // stderr: "inherit",
  /* 環境変数を渡す */
    // env: { MY_ENV: "hello" },
  /* trueにすると、子プロセスから親プロセスの環境変数を読み取れない(デフォルトはfalse) */
    // clearEnv: true,
  /* AbordContorollerやAbortSignalを使用して途中でサブプロセスを中断できる */
    // signal: AbortSignal.timeout(5000),
  /* Windowsでの引用符のエスケープなどの挙動を制御する */
    // windowsRawArguments: true,
});
const { code, success, signal, stdout, stderr } = await command.output();
console.log(code); // => 0
console.log(success); // => true
console.log(signal); // => null
console.log(decoder.decode(stdout)); // => hello
console.log(decoder.decode(stderr)); // => hello from stderr

無事コマンドを実行してstdout(標準出力)とstderr(標準エラー出力)を受け取ることができました。

stdin / stdout / stderrについては、それぞれに対して

  • "inherit":親プロセスから継承する
    • 親プロセスのstdinは直接子プロセスのstdinへ、子プロセスのstdoutは親プロセスのstdoutへ、stderrはstderrへ流れる。
  • "piped":プログラム側で制御する
    • stdinやstdoutの入出力をプログラム側から操作するときに指定する
  • "null": 何もしない

というオプションを渡すことができます。

デフォルトではstdinが"inherit"、stdoutとstderrが"piped"になっています。
ただし後述のストリーミング処理を行う場合、デフォルト値は全て"inherit"になります。

上記の例は単純にコマンドを実行して結果を受け取るだけでした。
command.spawn()を呼ぶことで、コマンドの入出力をストリーミングすることもできます。

const command = new Deno.Command(Deno.execPath(), {
  args: ["eval", "console.log('hello'); console.error('hello from stderr');"],
  // stdin: "piped",
  stdout: "piped",
  stderr: "piped",
});

// spawn()を呼ぶとストリーミング開始
const process = command.spawn();

// process.stdout や process.stderr は ReadableStream なので、ファイルにパイプしたりResponseに渡したりすることができる
process.stdout.pipeTo((await Deno.open("./tmp.txt")).writable);
new Response(process.stderr);

// プロセス終了時のステータスなどを取得
const { code, success, signal } = await process.status;
console.log(code); // => 0
console.log(success); // => true
console.log(signal); // => null

ストリーミング処理はWeb Stream APIを使って行うようになっています。DenoのIO系は全てWeb Stream APIに統一されているため、そのままファイルに書き込んだり、HTTPレスポンスに流したりといった事が可能です。

方法2:daxを使う

daxはシェルコマンド用のユーティリティライブラリです。
Deno.Commandの代わりにサブプロセスを起動するために使うことができます。
作者はDenoコアチームのdavidさんです。

まずは基本的な使い方です。Deno.Commandの例と比べると大分コード量が減っている感じがします。

dax使用例
import $ from "https://deno.land/x/dax@0.28.0/mod.ts";

const result = await $`deno eval 'console.log(1); console.error(2);'`;
console.log(result.code);

stdoutとstderrをプログラムで受け取るには、以下のようにします。

stdoutとstderrの受け取り方
import $ from "https://deno.land/x/dax@0.28.0/mod.ts";

const result = await $`deno eval 'console.log(1); console.error(2);'`
  .stdout("piped")
  .stderr("piped");

console.log(result.stdout); // 1\n
console.log(result.stderr); // 2\n

以上を見て分かる通り、実体としては上で紹介したDeno.Commandのラッパーですが、それに加えてクロスプラットフォーム対応も入っています。
通常、echoなどのコマンドを使ってしまうとWindowsでcommand not foundと怒られたりしますが、daxを使うとそれを回避することができます

import $ from "https://deno.land/x/dax@0.28.0/mod.ts";

// これはlinuxでもMac OSでもWindowsでも動く!
await $`echo 5`;

現在はcd / echo / exit / cp / mv / rm / mkdir / pwd / sleep / test / touchクロスプラットフォーム対応されています

なぜこれらが動くのかというと、Denoのタスクランナーの仕組み(こちらもクロスプラットフォーム)をそのまま持ってきて動かしてるからです。

なお、daxにはその他のユーティリティも付属しています。

// ディレクトリ移動
$.cd("path/to/dir");

// nミリ秒待機
await $.sleep(100);

// コマンドの絶対パスを見つける
console.log(await $.which("deno"));

// HTTPリクエストを送る
await $.request("https://example.com").json();

他のユーティリティも付属しているので、ぜひ公式ドキュメントをご覧ください。

まとめ

  • Deno.run()は非推奨になる予定
    • 長い間使用されていたこともあり移行期間がとられる予定のよう
  • 代わりにDeno.Command APIの使用が推奨されている
  • daxを使うと便利
    • クロスプラットフォーム対応なので、CIなど別環境で動かす用のスクリプトで便利そう

daxは手元でちょっと動かすやつとかCI用のスクリプトを動かすのにかなり便利そうなので使っていきたいと思います。

ちなみにdaxについてDenoコアチームのhashrockさんが解説した記事もあるので貼っておきます。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1