【Dart】コマンドライン引数解析ライブラリargsを試す
はじめに
この記事dart公式のコマンドライン引数の解析ライブラリである args を触りながらキャッチアップした際の記事になります。
このライブラリーはGNUとPOSIXスタイルのオプションをサポートしており、サーバーサイドとクライアントサイドの両方のアプリケーションで動作する。
-
GNUスタイルのオプション
-
POSIXスタイルのオプション
- オプションの指定方法:
- ハイフン(-)を使ってオプションを指定します
- 例:
-a
- オプションに値を渡す場合:
- 値とオプションはスペースで区切ります
- 例:
-f file.txt
- 短縮形の結合:
- POSIXでは通常、オプションは一度に一つずつ指定されます
- オプションの指定方法:
環境構築や準備
各バージョンや環境
$ sw_vers
ProductName: macOS
ProductVersion: 13.4.1
ProductVersionExtra: (c)
BuildVersion: 22F770820d
$ dart --version
Dart SDK version: 3.2.0 (stable) (Tue Nov 14 18:26:59 2023 +0000) on "macos_arm64"
pubspec.yaml
environment:
sdk: ^3.2.0
dependencies:
args: ^2.4.2
dev_dependencies:
lints: ^2.1.0
test: ^1.24.0
1. プロジェクト作成
argsを試す為にプロジェクトを作成します。
$ dart create -t console dart-args-example
$ cd dart-args-example
$ dart run
Building package executable...
Built dart_args_example:dart_args_example.
Hello world: 42!
2. argsパッケージ追加
以下コマンドで追加するか、
dart pub add args
pubspec.yaml
に以下を追加し dart pub get
を実施します。
dependencies:
args: ^2.4.2
実装
1. シンプルな実装
プロジェクト作成時に作成された bin/dart_args_example.dart
を以下に変更します。
import 'package:args/args.dart';
void main(List<String> arguments) {
var parser = ArgParser();
parser.addOption('mode', abbr: 'm');
parser.addFlag('verbose', abbr: 'v');
var results = parser.parse(arguments);
print('mode: ${results['mode']}');
print('verbose: ${results['verbose']}');
}
次のオプションを指定して実行してみます。
$ dart run bin/dart_args_example.dart -m debug -v
mode: debug
verbose: true
# 又は dart run bin/dart_args_example.dart --mode debug --verbose
上記の処理ではまずparse処理を行う ArgParser を作成しています。
次に特定の文字列等を指定する際に使用する addOption とフラグ値(true/false) を指定する際に使用する addFlag を使用してオプションを指定していきます。
最後に parse で引数を解析します。戻り値としては ArgResults が返却されます。
1-1. addOption
-
allowed
で設定できる値を指定できますparser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']);
-
defaultsTo
でデフォルト値を指定できますparser.addOption('mode', abbr: 'm', defaultsTo: 'debug');
1-2. addFlag
- デフォルトで、オプションを無効にする「no-」プレフィックスがつく
- 上記の例だと
dart run bin/dart_args_example.dart -m debug --no-verbose
と実行すると verboseがfalse
になります - longオプションのみ
- 上記の例だと
- 「no-」プレフィックスを無効にする
-
negatable
をfalse
で指定します。 parser.addFlag('verbose', abbr: 'v', negatable: false);
-
--no-verbose
を指定すると例外を投げる様になります
-
-
defaultsTo
でデフォルト値を指定できますparser.addFlag('verbose', abbr: 'v', defaultsTo: true);
コマンド定義
2.公式のドキュメントの例にあるように以下のコマンドを定義してみたいと思います。
dart run bin/dart_args_example.dart commit -a
新たに commit
コマンドを定義し、 commit
コマンドのオプションとして -a
を定義します。
コマンドは addCommand で定義できます。
void main(List<String> arguments) {
var parser = ArgParser();
parser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']);
parser.addFlag('verbose', abbr: 'v', defaultsTo: true);
// command: commit
var command = parser.addCommand('commit');
command.addFlag('all', abbr: 'a');
var results = parser.parse(arguments);
print('mode: ${results['mode']}');
print('verbose: ${results['verbose']}');
var commandResults = results.command;
if (commandResults != null) {
print('command: ${commandResults.name}');
print('command --all: ${commandResults['all']}');
}
}
以下のコマンドを実施してみます。
$ dart run bin/dart_args_example.dart -m debug -v commit -a
mode: debug
verbose: true
command: commit
command --all: true
コマンドの前のオプション指定はあまり無いかもですが、ちゃんとコマンドが解析できています。
ここで試しに2つコマンドを登録して、2つコマンドを指定して実行したらどうなるか試してみたいと思います。
void main(List<String> arguments) {
var parser = ArgParser();
parser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']);
parser.addFlag('verbose', abbr: 'v', defaultsTo: true);
// command: commit
var command = parser.addCommand('commit');
command.addFlag('all', abbr: 'a');
// command: stash
var stash = parser.addCommand('stash');
stash.addFlag('all', abbr: 'a');
var results = parser.parse(arguments);
print('mode: ${results['mode']}');
print('verbose: ${results['verbose']}');
var commandResults = results.command;
if (commandResults != null) {
print('command: ${commandResults.name}');
print('command --all: ${commandResults['all']}');
}
}
先ほどの実装に stash
コマンドを追加し、オプションは同じ all
を設定してみました。
これで実行してみると、
$ dart run bin/dart_args_example.dart -m debug -v stash --no-all commit -a
mode: debug
verbose: true
command: stash
command --all: true
コマンドに関しては先に指定したコマンドが採用されているのですが、オプションは最後の commit
の方の all
が採用されています。
コマンドのディスパッチ
3.コマンドベースのアプリケーションを作成する場合は、CommandRunnerクラスとCommandクラスを使用して特化した構成にすることができるそうです。
CommandRunner
3-1.- コマンドライン引数に基づいてCommandにディスパッチする機能
- フラグや無効な引数を処理するためのサポート
Command
3-2.- コマンドにサブコマンドがなく、実行することを目的としている場合、そのコマンドは「リーフ コマンド」と呼ばれる
- リーフコマンドはrunをオーバーライドする必要がある
- サブコマンドを含むコマンドは addSubcommand で登録する必要がある
3-3. CommandRunnerとCommandで置き換え
先ほどのサンプルをCommandRunnerとCommandで置き換えてみたいと思います。
先に CommitCommand
クラスと StashCommand
クラスを作成します。
-
CommitCommandクラス (
lib/commit_command.dart
)import 'package:args/command_runner.dart'; class CommitCommand extends Command { String get name => 'commit'; String get description => 'Record changes to the repository.'; CommitCommand() { // ここでコマンド固有の引数を追加できる。argParserは内部で作成される。 argParser.addFlag('all', abbr: 'a'); } void run() { print("CommitCommand --all: ${argResults?['all']}"); } }
-
StashCommandクラス (
lib/stash_command.dart
)import 'package:args/command_runner.dart'; class StashCommand extends Command { String get name => 'stash'; String get description => 'Stash changes in the working directory.'; StashCommand() { // ここでコマンド固有の引数を追加できる。argParserは内部で作成される。 argParser.addFlag('all', abbr: 'a'); } void run() { print("StashCommand --all: ${argResults?['all']}"); } }
bin/dart_args_example.dart
を以下の様に修正します。
void main(List<String> arguments) {
var runner = CommandRunner(
'dgit', "A dart implementation of distributed version control.");
runner.argParser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']);
runner.argParser.addFlag('verbose', abbr: 'v', defaultsTo: true);
runner.addCommand(CommitCommand());
runner.addCommand(StashCommand());
runner.run(arguments);
}
ドキュメントのサンプルにある様に dgit
というCLIアプリの想定で作成しています。
早速実行してみます。
$ dart run bin/dart_args_example.dart -m debug -v commit -a
CommitCommand --all: true
ちゃんと実行できてそうです!
ちなみに複数コマンドを指定する場合の挙動も変わらずでした。
$ dart run bin/dart_args_example.dart -m debug -v stash -a commit --no-all
StashCommand --all: false
コマンドに関しては先に指定したコマンドが採用されているのですが、オプションは最後の
commit
の方のall
が採用されています。
Discussion