表題のよくありがちな簡単な作業があったので、 Deno を触ってみるのにいい題材かなと思い使ってみました。
Deno のキャッチアップ
VSCode で Deno を書く準備
こちらの denoの標準ライブラリをVS Codeでいい感じに補完するためにやった作業 という記事を参考に、 deno --types
で生成できる標準モジュールの型定義と、~/.deno/deps
以下にDLされる外部モジュールへパスを通して補完するようになりました。
また、上記記事の方がこの設定を生成するツール denoinit というのも作っています。
実装してみる
とりあえず何を使ったらいいかみてみました
コマンドの引数を取得する
args
を使う方法
import { args } from "deno";
console.log(args);
$ deno args.ts param1 param2
[ "args.ts", "param1", "param2" ]
ファイルの内容を取得する
open
を使う方法
import { args, copy, open, stdout } from "deno";
(async () => {
const file = await open(args[1]);
await copy(stdout, file);
file.close();
})();
readFile
を使う方法
import { args, readFile, stdout } from "deno";
(async () => {
const data = await readFile(args[1]);
stdout.write(data);
})();
$ deno open.ts example.csv
1,a
2,b
$ deno readFile.ts example.csv
1,a
2,b
他のコマンドを実行する
対象の CSV が Shift_JIS なので文字コードを変換したかったのですが、 iconv 的なモジュールはないようで、自作したくないので、とりあえず Linux コマンドの iconv を実行することにしました。
run
を使って子プロセスを起動するようです。
import { args, run, stdout } from "deno";
(async () => {
const process = run({
args: ["iconv", "-f", "sjis", "-t", "utf8", args[1]],
stdout: "piped"
});
stdout.write(await process.output());
process.close();
})();
実行する際に子プロセス実行許可オプションを付けます。
$ deno --allow-run iconv.ts example.csv
1,a,あ
2,b,い
CSV をパースする
deno-fnparse
どうやら年末にもくもく会があったみたいですが、ちょうど CSV パーサが作られていてよかったです。
というわけで今日のもくもく会の成果。パーサコンビネータで書いたDeno用CSVパーサ。 #denotshttps://t.co/Y2tUznpP53
— はっしゅろっく (@hashedrock) 2018年12月29日
import { args, run } from "deno";
import { parseCsv } from "https://denopkg.com/hashrock/deno-fnparse/parsers/csv.ts";
(async () => {
const process = run({
args: ["iconv", "-f", "sjis", "-t", "utf8", args[1]],
stdout: "piped"
});
const csv = new TextDecoder().decode(await process.output());
process.close();
const rows = parseCsv(csv);
console.log(rows);
})();
$ deno --allow-run parse.ts example.csv
[ [ "1", "a", "あ" ], [ "2", "b", "い" ] ]
一通り書いたので実行可能にする
Shebang を付ける
#!/usr/bin/env deno --allow-run
import { args, run, stdout } from "deno";
import { parseCsv } from "https://denopkg.com/hashrock/deno-fnparse/parsers/csv.ts";
(async () => {
const process = run({
args: ["iconv", "-f", "sjis", "-t", "utf8", args[1]],
stdout: "piped"
});
const csv = new TextDecoder().decode(await process.output());
process.close();
const rows = parseCsv(csv) as string[][];
const table = args[1].slice(0, -4);
const encoder = new TextEncoder();
rows.forEach(row => {
stdout.write(
encoder.encode(`INSERT INTO ${table} VALUES ('${row.join("', '")}');\n`)
);
});
})();
$ chmod +x convert.ts
$ ./convert.ts example.csv
INSERT INTO examples VALUES ('1', 'a', 'あ');
INSERT INTO examples VALUES ('2', 'b', 'い');
以上です。
下記のようにディスカッションされていますが、この手の変換処理は本当は Reader / Writer ベースで処理を書いた方が望ましいと思うので宿題にしようと思います。
StreamはNodeにしか無いので、Denoだとio packageを使ってReader / Writerでデータをやり取りしていく感じですね…!
— しゅーまい (@__syumai) 2019年1月3日