コマンドラインアプリの作成
- スタンドアロンDart VMによるアプリの実行
- dcatアプリコードの概要
- コマンドライン引数の解析
- stdin、stdout、stderrによる読み書き
- ファイルに関する情報の取得
- ファイルの読み込み
- ファイルへの書き込み
- 環境情報の取得
- 終了コードの設定
- まとめ
- 次のステップ
このチュートリアルでは、コマンドラインアプリの構築方法を学び、いくつかの小さなコマンドラインアプリケーションを紹介します。これらのプログラムは、標準出力、標準エラー、標準入力ストリーム、コマンドライン引数、ファイルとディレクトリなど、ほとんどのコマンドラインアプリケーションに必要なリソースを使用しています。
スタンドアロンDart VMによるアプリの実行
#Dart VMでコマンドラインアプリを実行するには、dart run
を使用します。dart
コマンドはDart SDKに含まれています。
小さなプログラムを実行してみましょう。
このコードを含む
hello_world.dart
というファイルを作成しますdartvoid main() { print('Hello, World!'); }
作成したファイルを含むディレクトリで、プログラムを実行します
$ dart run hello_world.dart Hello, World!
Dartツールは多くのコマンドとオプションをサポートしています。一般的に使用されるコマンドとオプションを表示するには、dart --help
を使用します。すべてのオプションを表示するには、dart --verbose
を使用します。
dcatアプリコードの概要
#このチュートリアルでは、コマンドラインで指定されたファイルの内容を表示するdcat
という小さなサンプルアプリの詳細について説明します。このアプリは、コマンドラインアプリで使用可能なさまざまなクラス、関数、プロパティを使用しています。チュートリアルを進めて、アプリの各部分と使用されているさまざまなAPIについて学びましょう。
import 'dart:convert';
import 'dart:io';
import 'package:args/args.dart';
const lineNumber = 'line-number';
void main(List<String> arguments) {
exitCode = 0; // Presume success
final parser = ArgParser()..addFlag(lineNumber, negatable: false, abbr: 'n');
ArgResults argResults = parser.parse(arguments);
final paths = argResults.rest;
dcat(paths, showLineNumbers: argResults[lineNumber] as bool);
}
Future<void> dcat(List<String> paths, {bool showLineNumbers = false}) async {
if (paths.isEmpty) {
// No files provided as arguments. Read from stdin and print each line.
await stdin.pipe(stdout);
} else {
for (final path in paths) {
var lineNumber = 1;
final lines = utf8.decoder
.bind(File(path).openRead())
.transform(const LineSplitter());
try {
await for (final line in lines) {
if (showLineNumbers) {
stdout.write('${lineNumber++} ');
}
stdout.writeln(line);
}
} catch (_) {
await _handleError(path);
}
}
}
}
Future<void> _handleError(String path) async {
if (await FileSystemEntity.isDirectory(path)) {
stderr.writeln('error: $path is a directory');
} else {
exitCode = 2;
}
}
依存関係の取得
#dcatはargsというパッケージに依存していることに気付くかもしれません。argsパッケージを取得するには、pubパッケージマネージャーを使用します。
実際のアプリには、テスト、ライセンスファイル、依存関係ファイル、例などが含まれています。しかし、最初のアプリでは、dart create
コマンドを使用して必要なものだけを簡単に作成できます。
ディレクトリ内で、dartツールを使用してdcatアプリを作成します。
$ dart create dcat
作成したディレクトリに移動します。
$ cd dcat
dcat
ディレクトリ内で、dart pub add
を使用してargs
パッケージを依存関係として追加します。これにより、pubspec.yaml
ファイルにある依存関係のリストにargs
が追加されます。$ dart pub add args
bin/dcat.dart
ファイルを開き、上記のコードをコピーします。
dcatの実行
#アプリの依存関係が揃ったら、コマンドラインから任意のテキストファイル(例:pubspec.yaml
)に対してアプリを実行できます。
$ dart run bin/dcat.dart -n pubspec.yaml
1 name: dcat
2 description: A sample command-line application.
3 version: 1.0.0
4 # repository: https://github.com/my_org/my_repo
5
6 environment:
7 sdk: ^3.5.3
8
9 # Add regular dependencies here.
10 dependencies:
11 args: ^2.5.0
12 # path: ^1.8.0
13
14 dev_dependencies:
15 lints: ^4.0.0
16 test: ^1.24.0
このコマンドは、指定されたファイルの各行を表示します。-n
オプションを指定したため、各行の前に行番号が表示されます。
コマンドライン引数の解析
#argsパッケージは、コマンドライン引数をオプション、フラグ、追加値のセットに変換するためのパーサーサポートを提供します。argsライブラリを次のようにインポートします
import 'package:args/args.dart';
args
ライブラリには、他にもいくつかのクラスが含まれています。
クラス | 説明 |
---|---|
ArgParser | コマンドライン引数パーサー。 |
ArgResults | ArgParser を使用してコマンドライン引数を解析した結果。 |
dcat
アプリの次のコードでは、これらのクラスを使用して、指定されたコマンドライン引数を解析して格納します。
void main(List<String> arguments) {
exitCode = 0; // Presume success
final parser = ArgParser()..addFlag(lineNumber, negatable: false, abbr: 'n');
ArgResults argResults = parser.parse(arguments);
final paths = argResults.rest;
dcat(paths, showLineNumbers: argResults[lineNumber] as bool);
}
Dartランタイムは、コマンドライン引数を文字列のリストとしてアプリのmain
関数に渡します。ArgParser
は-n
オプションを解析するように構成されています。次に、コマンドライン引数を解析した結果がargResults
に格納されます。
次の図は、上記で使用されたdcat
コマンドラインがArgResults
オブジェクトにどのように解析されるかを示しています。
Map
のようにArgResults
を扱うことで、名前でフラグとオプションにアクセスできます。他の値には、rest
プロパティを使用してアクセスできます。
args
ライブラリのAPIリファレンスには、ArgParser
クラスとArgResults
クラスの使用に役立つ詳細な情報が記載されています。
stdin、stdout、stderrによる読み書き
#他の言語と同様に、Dartには標準出力、標準エラー、標準入力ストリームがあります。標準I/Oストリームは、dart:io
ライブラリの最上位レベルで定義されています。
dart:io
ライブラリを次のようにインポートします。
import 'dart:io';
stdout
#dcat
アプリの以下のコードは、(-n
オプションが指定されている場合)行番号をstdout
に出力し、その後に行の内容を出力します。
if (showLineNumbers) {
stdout.write('${lineNumber++} ');
}
stdout.writeln(line);
write()
メソッドとwriteln()
メソッドは、任意の型のオブジェクトを受け取り、それを文字列に変換して出力します。writeln()
メソッドは改行文字も出力します。dcat
アプリは行番号を出力するためにwrite()
メソッドを使用しているので、行番号とテキストは同じ行に表示されます。
オブジェクトのリストを出力するにはwriteAll()
メソッドを使用することも、ストリームからすべての要素を非同期的に出力するにはaddStream()
メソッドを使用することもできます。
stdout
はprint()
関数よりも多くの機能を提供します。たとえば、stdout
を使用してストリームの内容を表示できます。ただし、ウェブ上で実行されるアプリの場合は、stdout
ではなくprint()
を使用する必要があります。
stderr
#エラーメッセージをコンソールに出力するにはstderr
を使用します。標準エラーストリームはstdout
と同じメソッドを持ち、同じように使用します。stdout
とstderr
の両方ともコンソールに出力しますが、それらの出力は別々であり、コマンドラインまたはプログラムで異なる宛先にリダイレクトまたはパイプできます。
dcat
アプリの以下のコードは、ユーザーがファイルではなくディレクトリの行を出力しようとすると、エラーメッセージを出力します。
if (await FileSystemEntity.isDirectory(path)) {
stderr.writeln('error: $path is a directory');
} else {
exitCode = 2;
}
stdin
#標準入力ストリームは通常、キーボードからデータを同期的に読み取りますが、非同期的に読み取ったり、別のプログラムの標準出力からパイプされた入力を受け取ったりすることもできます。
stdin
から1行を読み取る小さなプログラムを次に示します。
import 'dart:io';
void main() {
stdout.writeln('Type something');
final input = stdin.readLineSync();
stdout.writeln('You typed: $input');
}
readLineSync()
メソッドは標準入力ストリームからテキストを読み取り、ユーザーがテキストを入力してリターンキーを押すまでブロックします。この小さなプログラムは入力されたテキストを出力します。
dcat
アプリでは、ユーザーがコマンドラインにファイル名を指定しない場合、プログラムは代わりにpipe()
メソッドを使用してstdinから読み取ります。pipe()
は非同期であるため(このコードはその戻り値を使用しませんが、Future
を返します)、それを呼び出すコードはawait
を使用します。
await stdin.pipe(stdout);
この場合、ユーザーはテキストの行を入力し、アプリはその行をstdoutにコピーします。Control+D(またはWindowsではControl+Z)を押すと、入力の終了を知らせます。
$ dart run bin/dcat.dart
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
ファイルに関する情報の取得
#dart:io
ライブラリのFileSystemEntity
クラスは、ファイルシステムの検査と操作に役立つプロパティと静的メソッドを提供します。
たとえば、パスがある場合、FileSystemEntity
クラスのtype()
メソッドを使用して、そのパスがファイル、ディレクトリ、リンク、または見つからないものかどうかを判断できます。type()
メソッドはファイルシステムにアクセスするため、非同期的にチェックを実行します。
dcat
アプリの以下のコードは、FileSystemEntity
を使用して、コマンドラインで指定されたパスがディレクトリかどうかを判断します。返されたFuture
は、そのパスがディレクトリかどうかを示すブール値で完了します。チェックが非同期であるため、コードはawait
を使用してisDirectory()
を呼び出します。
if (await FileSystemEntity.isDirectory(path)) {
stderr.writeln('error: $path is a directory');
} else {
exitCode = 2;
}
FileSystemEntity
クラスの他の興味深いメソッドには、isFile()
、exists()
、stat()
、delete()
、rename()
などがあり、これらもすべてFuture
を使用して値を返します。
FileSystemEntity
は、File
、Directory
、Link
クラスのスーパークラスです。
ファイルの読み込み
#dcat
アプリは、openRead()
メソッドを使用してコマンドラインにリストされている各ファイルを開き、これはStream
を返します。await for
ブロックは、ファイルが非同期的に読み取られデコードされるのを待ちます。データがストリームで利用可能になると、アプリはそれをstdoutに出力します。
for (final path in paths) {
var lineNumber = 1;
final lines = utf8.decoder
.bind(File(path).openRead())
.transform(const LineSplitter());
try {
await for (final line in lines) {
if (showLineNumbers) {
stdout.write('${lineNumber++} ');
}
stdout.writeln(line);
}
} catch (_) {
await _handleError(path);
}
}
以下は、コードの残りの部分を強調したもので、await for
ブロックで利用可能になる前にデータを変換する2つのデコーダーを使用しています。UTF8デコーダーはデータをDart文字列に変換します。LineSplitter
は改行でデータを分割します。
for (final path in paths) {
var lineNumber = 1;
final lines = utf8.decoder
.bind(File(path).openRead())
.transform(const LineSplitter());
try {
await for (final line in lines) {
if (showLineNumbers) {
stdout.write('${lineNumber++} ');
}
stdout.writeln(line);
}
} catch (_) {
await _handleError(path);
}
}
dart:convert
ライブラリはこれらと他のデータコンバーター(JSON用のコンバーターを含む)を提供します。これらのコンバーターを使用するには、dart:convert
ライブラリをインポートする必要があります。
import 'dart:convert';
ファイルへの書き込み
#テキストをファイルに書き込む最も簡単な方法は、File
オブジェクトを作成してwriteAsString()
メソッドを使用することです。
final quotes = File('quotes.txt');
const stronger = 'That which does not kill us makes us stronger. -Nietzsche';
await quotes.writeAsString(stronger, mode: FileMode.append);
writeAsString()
メソッドはデータを非同期的に書き込みます。書き込み前にファイルを開き、完了時にファイルを閉じます。既存のファイルにデータを追加するには、オプションの命名パラメーターmode
を使用し、その値をFileMode.append
に設定できます。それ以外の場合は、モードはFileMode.write
にデフォルトになり、ファイルの以前の内容(存在する場合)は上書きされます。
より多くのデータを書きたい場合は、書き込みのためにファイルを開くことができます。openWrite()
メソッドはIOSink
を返し、これはstdinとstderrと同じ型です。openWrite()
から返されたIOSink
を使用すると、完了するまでファイルに書き込み続け、その時点でファイルを閉じなければなりません。close()
メソッドは非同期であり、Future
を返します。
final quotes = File('quotes.txt').openWrite(mode: FileMode.append);
quotes.write("Don't cry because it's over, ");
quotes.writeln('smile because it happened. -Dr. Seuss');
await quotes.close();
環境情報の取得
#Platform
クラスを使用して、アプリが実行されているマシンとオペレーティングシステムに関する情報を取得します。
静的Platform.environment
プロパティは、環境変数の複製を不変のマップで提供します。変更可能なマップ(変更可能なコピー)が必要な場合は、Map.of(Platform.environment)
を使用できます。
final envVarMap = Platform.environment;
print('PWD = ${envVarMap['PWD']}');
print('LOGNAME = ${envVarMap['LOGNAME']}');
print('PATH = ${envVarMap['PATH']}');
Platform
は、マシン、OS、現在実行中のアプリに関する情報を提供する他の便利なプロパティを提供します。たとえば
終了コードの設定
#dart:io
ライブラリは、トップレベルのプロパティexitCode
を定義しており、これを使用してDart VMの現在の呼び出しの終了コードを設定できます。終了コードとは、Dartアプリから親プロセスに渡される数値で、アプリの実行の成功、失敗、またはその他の状態を示します。
dcat
アプリは、_handleError()
関数で終了コードを設定して、実行中にエラーが発生したことを示します。
Future<void> _handleError(String path) async {
if (await FileSystemEntity.isDirectory(path)) {
stderr.writeln('error: $path is a directory');
} else {
exitCode = 2;
}
}
終了コード2
は、アプリでエラーが発生したことを示します。
exitCode
を使用する代わりに、トップレベルのexit()
関数を使用できます。これは終了コードを設定し、アプリをすぐに終了します。たとえば、_handleError()
関数は、exitCode
を2に設定する代わりにexit(2)
を呼び出すことができますが、exit()
はプログラムを終了し、実行中のコマンドで指定されたすべてのファイルを処理できない可能性があります。
終了コードには任意の数値を使用できますが、慣例により、以下の表のコードには次の意味があります。
コード | 意味 |
---|---|
0 | 成功 |
1 | 警告 |
2 | エラー |
まとめ
#このチュートリアルでは、dart:io
ライブラリの以下のクラスにある基本的なAPIについて説明しました。
API | 説明 |
---|---|
IOSink | ストリームからデータを読み取るオブジェクトのヘルパークラス |
File | ネイティブファイルシステム上のファイルを表現します |
Directory | ネイティブファイルシステム上のディレクトリを表します |
FileSystemEntity | FileとDirectoryのスーパークラス |
Platform | マシンとオペレーティングシステムに関する情報を提供します |
stdout | 標準出力ストリーム |
stderr | 標準エラーストリーム |
stdin | 標準入力ストリーム |
exitCode | 終了コードへのアクセスと設定 |
exit() | 終了コードを設定して終了します |
さらに、このチュートリアルでは、コマンドライン引数の解析と使用に役立つpackage:args
の2つのクラス、ArgParser
とArgResults
について説明しました。
クラス、関数、プロパティの詳細については、dart:io
、dart:convert
、package:args
のAPIドキュメントを参照してください。
コマンドラインアプリの別の例については、command_line
サンプルを確認してください。
次のステップ
#サーバーサイドプログラミングに興味がある場合は、次のチュートリアルを確認してください。
特に明記されていない限り、このサイトのドキュメントはDart 3.5.3を反映しています。ページの最終更新日は2024年9月16日です。 ソースを見る または 問題を報告する。