🐙

Dartでコマンドラインアプリを作ってみる

2024/06/19に公開

キッカケ

AtCoderで遊んでいる時にDartを選択し、そもそも標準入力はどうやるんだ?と思いました。
考えてみるとFlutterでDartを触り始めましたが、モバイルアプリを作ること以外で使ったことがなく、Dartについてあまり知らないんじゃないかと思い、Dartのプログラムを組んでみることにしました。

プロジェクトの作り方

前提としてFlutterをインストールしています。

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.22.2, on macOS 13.6 22G120 darwin-arm64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.2)
[✓] VS Code (version 1.90.1)
[✓] Connected device (6 available)            
[✓] Network resources

• No issues found!

この状態で既にdartコマンドを実行できるようになっています。
dart言語単体のプロジェクトは以下のコマンドで作成で出来ました。(exampleは任意の名称で)

$ flutter create hallo_world

Hallo World

プロジェクト作成時にFlutterプロジェクト作成時と同様に以下で起動できると言われたのでその通りにまずは実行してみます。

$ cd hallo_world
$ dart run

実行するとコンソールに以下に表示されました。
デフォルトではHello world: 42!と表示されるプロジェクトが作成されているようです。

$ dart run
Building package executable... 
Built hallo_world:hallo_world.
Hello world: 42!

プロジェクトは以下の構成になっていました。

bin/hallo_world.dart

import 'package:hallo_world/hallo_world.dart' as hallo_world;

void main(List<String> arguments) {
  print('Hello world: ${hallo_world.calculate()}!');
}

lib/hallo_world.dart

int calculate() {
  return 6 * 7;
}

binには実行されるmain関数があり、libはロジックが書かれているので、
基本的にはbinにもろもろ実装し、ロジック実装はlib内に定義、開発している感じだろうと思います。
(testは言わずもがな)

引数

main関数にargumentsがあるので、実行時に引数を渡せるのだろうと、引数を表示して実行してみました。

void main(List<String> arguments) {
  for (var arg in arguments) {
    print(arg);
  }
}
$ dart run hallo_world aaaa bbbbb ccccc
Building package executable... 
Built hallo_world:hallo_world.
Hello world
aaaa
bbbbb
ccccc

ちなみに引数がある場合はhallo_worldのようにプロジェクト名を指定する必要がありそうでした。

$ dart run aaaa bbbbb ccccc
Could not find package `aaaa` or file `aaaa`

また引数の文字列に意図してスペースを与える場合は''(もしくは"")で囲みます。

$ dart run hallo_world 'a aaa' "bb  bbb" "ccc   cc"
Building package executable... 
Built hallo_world:hallo_world.
a aaa
bb  bbb
ccc   cc

標準入力

プログラム実行後、入力待ち状態にしてコマンドライン上で入力を待ちそれを表示します。
AtCoderの場合は入力をstdin.readLineSync()で一行ずつ読み込むようでした。

import 'dart:io';

void main(List<String> arguments) async {
  while (true) {
    final input = stdin.readLineSync();
    if (input == 'exit') {
      break;
    }

    print('output:$input');
  }
}

ファイル読み込み

実行時にファイル名を渡して読み込ませ、内容を表示してみます。

import 'dart:io';

void main(List<String> arguments) async {
  final path = arguments[0];
  final file = File(path);
  final lines = await file.readAsLines();
  for (var line in lines) {
    print(line);
  }
}
$ dart run hallo_world input.txt
Building package executable... 
Built hallo_world:hallo_world.
111111
222222
333333

引数に指定したinput.txtはプロジェクトのルートに置いています。

感想

標準入力するプログラムは10年以上昔にC言語やJavaを勉強した時に触ったとき以来で懐かしかったです。
アプリを作る場合は各OSが用意してくれるフレームワーク上にプログラムを書くのが当たり前になっていたので、たまにはcli的なアプリを作るものいいかもと思いました。

Discussion