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
の例と比べると大分コード量が減っている感じがします。
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をプログラムで受け取るには、以下のようにします。
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さんが解説した記事もあるので貼っておきます。