C#コマンドライン入門|引数の受け取り方から実行・オプション解析まで初心者向けに解説
はじめに
C#でコンソールアプリを作るとき、避けて通れないのが「コマンドライン引数」です。たとえば、次のようにプログラムを実行するとします。
Bashdotnet run -- Tanaka
このとき Tanaka という値をC#プログラム側で受け取り、名前の表示、ファイルの読み込み、処理モードの切り替えなどに使えます。
C#コマンドラインアプリは、GUIアプリよりもシンプルに作れるため、学習用の小さなプログラム、社内ツール、自動処理、バッチ処理、ファイル変換ツールなどに向いています。この記事では、C#でコマンドライン引数を受け取る基本から、dotnet runでの実行方法、オプション解析、System.CommandLineを使った本格的な実装まで、初心者向けに順番に解説します。
1. C#のコマンドライン引数とは?まず押さえる基本
1-1. コマンドライン引数とは何か
コマンドライン引数とは、プログラムを起動するときに一緒に渡す文字列のことです。
たとえば、次のコマンドを見てください。
Bashmyapp.exe apple banana orange
この場合、apple、banana、orange がコマンドライン引数です。C#では、これらの値を string[] args という文字列配列として受け取れます。
C#static void Main(string[] args)
{
Console.WriteLine(args[0]); // apple
Console.WriteLine(args[1]); // banana
Console.WriteLine(args[2]); // orange
}
コマンドライン引数は、ユーザーがプログラムの動作を外から指定するための仕組みです。プログラム内に値を直接書き込まず、実行時に変更できる点が大きなメリットです。
1-2. C#でコマンドライン引数を使う場面
C#でコマンドライン引数を使う代表的な場面は、次のようなケースです。
| 用途 | 実行例 |
|---|---|
| 名前を渡す | myapp.exe Taro |
| ファイルパスを渡す | myapp.exe input.txt |
| 数値を渡す | myapp.exe 100 200 |
| 処理モードを指定する | myapp.exe --mode fast |
| フラグを指定する | myapp.exe --verbose |
たとえば、ファイル変換ツールなら次のように使えます。
Bashconvert.exe input.csv output.json
この場合、input.csv を読み込み、output.json に変換結果を書き出す、という処理をC#側で実装できます。
1-3. 引数・オプション・コマンドの違い
初心者が混乱しやすいのが、「引数」「オプション」「コマンド」の違いです。
| 種類 | 例 | 意味 |
|---|---|---|
| 引数 | input.txt | プログラムに渡す値 |
| オプション | --name Taro | 名前付きで渡す設定値 |
| フラグ | --verbose | true/falseを切り替える指定 |
| コマンド | add、delete | 実行する処理の種類 |
たとえば、次のコマンドを考えます。
Bashtodo.exe add "買い物に行く" --priority high
この場合、add はコマンド、"買い物に行く" は引数、--priority high はオプションです。
小さなC#コマンドラインアプリなら args を順番に読むだけでも十分です。一方で、複数のオプションやサブコマンドを扱う場合は、後半で紹介する System.CommandLine のようなライブラリを使うと管理しやすくなります。
1-4. 初心者がつまずきやすいポイント
C#コマンドラインで初心者がよくつまずくポイントは、主に次の5つです。
1つ目は、args[0] をいきなり参照してエラーになることです。引数が渡されていない状態で args[0] を読むと、配列の範囲外アクセスになります。
C#Console.WriteLine(args[0]); // 引数がないとエラー
2つ目は、スペースを含む文字列を引用符で囲まないことです。
Bashmyapp.exe Tokyo Station
この場合、Tokyo と Station は別々の引数になります。1つの文字列として渡したい場合は、次のようにします。
Bashmyapp.exe "Tokyo Station"
3つ目は、dotnet run に渡す引数と、自分のアプリに渡す引数を混同することです。dotnet run では、アプリ側に渡したい引数を -- の後ろに書くのが基本です。Microsoft Learnでも、dotnet run の引数とアプリケーション引数を分けるために -- を使うと説明されています。
Bashdotnet run -- apple banana
4つ目は、数値も最初は文字列として受け取る点です。100 と入力しても、C#側では "100" という文字列なので、計算に使うには int.Parse や int.TryParse で変換する必要があります。
5つ目は、Windows、macOS、Linux、PowerShell、コマンドプロンプト、Bashなど、実行環境によって引用符やパスの扱いが少し違うことです。まずは基本の受け取り方を理解し、そのあと自分の利用環境で動作確認することが大切です。
2. C#でコマンドライン引数を受け取る方法
2-1. Mainメソッドのstring[] argsで受け取る
C#でコマンドライン引数を受け取る基本形は、Main メソッドに string[] args を書く方法です。
C#using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("引数の数: " + args.Length);
foreach (string arg in args)
{
Console.WriteLine(arg);
}
}
}
実行例は次のとおりです。
Bashdotnet run -- apple banana orange
出力例です。
引数の数: 3
apple
banana
orange
args は文字列配列です。つまり、1つ目の引数は args[0]、2つ目の引数は args[1]、3つ目の引数は args[2] で取得できます。
2-2. argsの中身を確認する基本コード
まずは、受け取った引数をすべて表示するコードを書いてみましょう。
C#using System;
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"args[{i}] = {args[i]}");
}
}
}
次のように実行します。
Bashdotnet run -- red green blue
出力は次のようになります。
args[0] = red
args[1] = green
args[2] = blue
コマンドライン引数の学習では、まず args に何が入っているかを表示するのが一番わかりやすい確認方法です。
2-3. args.Lengthで引数の有無を判定する
引数を安全に扱うには、必ず args.Length を確認します。
C#using System;
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("引数を指定してください。");
return;
}
Console.WriteLine($"最初の引数: {args[0]}");
}
}
このように書けば、引数がないときに args[0] を読んでエラーになることを防げます。
複数の引数が必要な場合は、必要数をチェックします。
C#if (args.Length < 2)
{
Console.WriteLine("使い方: myapp <入力ファイル> <出力ファイル>");
return;
}
string inputPath = args[0];
string outputPath = args[1];
初心者のうちは、「args を使う前に args.Length を確認する」と覚えておきましょう。
2-4. トップレベルステートメントでargsを使う方法
最近のC#では、class Program や static void Main を書かずに、トップレベルステートメントでプログラムを書けます。
C#Console.WriteLine($"引数の数: {args.Length}");
foreach (string arg in args)
{
Console.WriteLine(arg);
}
これだけで、args を使えます。Program.cs にこのコードを書き、次のように実行します。
Bashdotnet run -- hello world
出力例です。
引数の数: 2
hello
world
学習中は Main メソッド形式のほうが仕組みを理解しやすいですが、短いサンプルや小さなツールではトップレベルステートメントも便利です。
2-5. Environment.GetCommandLineArgsとの違い
C#では、Environment.GetCommandLineArgs() を使ってコマンドライン引数を取得することもできます。
C#string[] values = Environment.GetCommandLineArgs();
foreach (string value in values)
{
Console.WriteLine(value);
}
string[] args との大きな違いは、Environment.GetCommandLineArgs() には実行ファイル名や実行パスに相当する値も含まれることです。
たとえば、通常の args では次のようになります。
args[0] = apple
args[1] = banana
一方、Environment.GetCommandLineArgs() では、先頭にプログラム自身のパスが入ることがあります。
values[0] = C:\sample\myapp.exe
values[1] = apple
values[2] = banana
通常の引数処理では string[] args を使えば十分です。実行ファイル自身のパスも含めて確認したい場合に、Environment.GetCommandLineArgs() を使うとよいでしょう。
3. C#プログラムをコマンドラインから実行する手順
3-1. .NETプロジェクトを作成する
まずはC#のコンソールアプリを作成します。.NET SDKがインストールされている状態で、ターミナルやコマンドプロンプトを開き、次のコマンドを実行します。
Bashdotnet new console -n CommandLineSample
作成されたフォルダに移動します。
Bashcd CommandLineSample
Program.cs を開くと、コンソールアプリのコードが入っています。ここにコマンドライン引数を受け取るコードを書いていきます。
.NETはWindows、Linux、macOSで利用できるクロスプラットフォームな開発環境として提供されています。
3-2. dotnet runで実行する
作成したプロジェクトは、次のコマンドで実行できます。
Bashdotnet run
dotnet run は、ソースコードをビルドしてから実行してくれる便利なコマンドです。Microsoft Learnでは、dotnet run は明示的なコンパイルや起動コマンドなしでソースコードを実行するコマンドと説明されています。
たとえば Program.cs を次のようにします。
C#Console.WriteLine("Hello, command line!");
実行すると、次のように表示されます。
Hello, command line!
3-3. dotnet runで引数を渡す方法
dotnet run でC#プログラムに引数を渡す場合は、基本的に -- の後ろに引数を書きます。
Bashdotnet run -- apple banana
Program.cs を次のようにします。
C#foreach (string arg in args)
{
Console.WriteLine(arg);
}
出力例です。
apple
banana
重要なのは、-- より前は dotnet run に対する指定、-- より後ろは自分のC#アプリに渡す指定、という点です。
たとえば、Release構成で実行しつつ、アプリには Taro を渡したい場合は次のようにします。
Bashdotnet run --configuration Release -- Taro
このとき、--configuration Release は dotnet run の設定で、Taro はC#アプリ側の args に入ります。
3-4. exeファイルに引数を渡して実行する方法
ビルド済みの実行ファイルに直接引数を渡すこともできます。
まずビルドします。
Bashdotnet build
Windowsの場合、出力フォルダに移動して、次のように実行できます。
Bashbin\Debug\net8.0\CommandLineSample.exe apple banana
macOSやLinuxでは、DLLを dotnet で実行する形になることもあります。
Bashdotnet bin/Debug/net8.0/CommandLineSample.dll apple banana
配布用に発行した場合は、発行先の実行ファイルに引数を渡します。
BashCommandLineSample.exe input.txt output.txt
dotnet run は開発中に便利ですが、本番利用や配布では dotnet publish で発行した成果物を実行するのが一般的です。
3-5. Visual Studioでコマンドライン引数を設定する方法
Visual Studioでデバッグ実行するときにも、コマンドライン引数を設定できます。
一般的な流れは次のとおりです。
プロジェクトを右クリックする
「プロパティ」を開く
「デバッグ」関連の設定を開く
コマンドライン引数の欄に値を入力する
F5キーまたはデバッグ実行する
たとえば、コマンドライン引数に次のように入力します。
apple banana "Tokyo Station"
この状態でデバッグ実行すると、args には次のように入ります。
args[0] = apple
args[1] = banana
args[2] = Tokyo Station
Visual Studioのバージョンによって画面名は多少異なりますが、「デバッグ」「起動プロファイル」「コマンドライン引数」などの項目を探すと設定できます。
4. コマンドライン引数の扱い方をサンプルで理解する
4-1. 文字列の引数を受け取る
もっとも基本的なのは、文字列を受け取って表示する処理です。
C#if (args.Length == 0)
{
Console.WriteLine("名前を指定してください。");
return;
}
string name = args[0];
Console.WriteLine($"こんにちは、{name}さん!");
実行例です。
Bashdotnet run -- Taro
出力例です。
こんにちは、Taroさん!
コマンドライン引数はすべて文字列として渡されるため、名前、メッセージ、ファイル名などの文字列はそのまま扱えます。
4-2. 数値の引数を受け取って変換する
数値を受け取る場合も、最初は文字列です。計算に使うには変換が必要です。
C#if (args.Length < 2)
{
Console.WriteLine("使い方: myapp <数値1> <数値2>");
return;
}
int a = int.Parse(args[0]);
int b = int.Parse(args[1]);
Console.WriteLine($"合計: {a + b}");
実行例です。
Bashdotnet run -- 10 20
出力例です。
合計: 30
ただし、int.Parse は変換できない文字列が来ると例外になります。実用的には int.TryParse を使うほうが安全です。
C#if (!int.TryParse(args[0], out int a))
{
Console.WriteLine("1つ目の引数は数値で指定してください。");
return;
}
if (!int.TryParse(args[1], out int b))
{
Console.WriteLine("2つ目の引数は数値で指定してください。");
return;
}
Console.WriteLine($"合計: {a + b}");
4-3. 複数の引数を順番に処理する
複数の引数をまとめて処理したい場合は、foreach が便利です。
C#if (args.Length == 0)
{
Console.WriteLine("1つ以上の値を指定してください。");
return;
}
foreach (string item in args)
{
Console.WriteLine($"受け取った値: {item}");
}
実行例です。
Bashdotnet run -- apple banana orange
出力例です。
受け取った値: apple
受け取った値: banana
受け取った値: orange
順番に意味がある場合は、インデックスで取り出します。
C#string inputFile = args[0];
string outputFile = args[1];
Console.WriteLine($"入力: {inputFile}");
Console.WriteLine($"出力: {outputFile}");
このような設計では、ユーザーに使い方を明示することが重要です。
使い方: myapp <入力ファイル> <出力ファイル>
4-4. スペースを含む文字列を引数として渡す
スペースを含む文字列を1つの引数として渡すには、引用符で囲みます。
Bashdotnet run -- "Tokyo Station"
C#側では、args[0] に Tokyo Station が入ります。
C#Console.WriteLine(args[0]);
出力例です。
Tokyo Station
引用符で囲まない場合は、次のように別々の引数として扱われます。
Bashdotnet run -- Tokyo Station
この場合は次のようになります。
args[0] = Tokyo
args[1] = Station
ファイルパスにもスペースが含まれることがあります。
Bashdotnet run -- "C:\Users\Taro\My Documents\input.txt"
Windowsのパスやフォルダ名にはスペースが含まれることがあるため、パスを引数で渡すときは引用符で囲む習慣をつけると安全です。
4-5. ファイルパスを引数として受け取る
ファイルパスを受け取り、存在するか確認するサンプルです。
C#if (args.Length == 0)
{
Console.WriteLine("ファイルパスを指定してください。");
return;
}
string path = args[0];
if (!File.Exists(path))
{
Console.WriteLine($"ファイルが見つかりません: {path}");
return;
}
string text = File.ReadAllText(path);
Console.WriteLine(text);
実行例です。
Bashdotnet run -- input.txt
スペースを含むパスの場合は、次のようにします。
Bashdotnet run -- "C:\sample folder\input.txt"
ファイルを扱うC#コマンドラインアプリでは、次のチェックを入れると実用性が上がります。
| チェック項目 | 内容 |
|---|---|
| 引数があるか | args.Length で確認 |
| ファイルが存在するか | File.Exists で確認 |
| 読み込み可能か | 例外処理で確認 |
| パスが空でないか | string.IsNullOrWhiteSpace で確認 |
5. C#でコマンドラインオプションを解析する基本
5-1. --nameや-fのようなオプションとは
コマンドラインツールでは、次のような形式をよく使います。
Bashmyapp.exe --name Taro --age 20
この --name や --age がオプションです。短い形式として、次のように書くこともあります。
Bashmyapp.exe -n Taro -a 20
一般的には、次のように使い分けます。
| 形式 | 例 | 特徴 |
|---|---|---|
| ロングオプション | --name | 意味がわかりやすい |
| ショートオプション | -n | 短く書ける |
| フラグ | --verbose | 指定されたかどうかを見る |
| 値付きオプション | --file input.txt | オプション名と値をセットで扱う |
単純な引数だけなら args[0]、args[1] で十分ですが、引数の数が増えると順番に依存する設計は使いにくくなります。そこで、--name のような名前付きオプションを使うと、わかりやすいコマンドラインになります。
5-2. 手動でオプションを解析する方法
まずは、手動で --name オプションを解析してみます。
C#string? name = null;
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "--name" && i + 1 < args.Length)
{
name = args[i + 1];
i++;
}
}
if (name == null)
{
Console.WriteLine("--name を指定してください。");
return;
}
Console.WriteLine($"こんにちは、{name}さん!");
実行例です。
Bashdotnet run -- --name Taro
出力例です。
こんにちは、Taroさん!
ポイントは、--name の次の要素を値として読むことです。
args[0] = --name
args[1] = Taro
ただし、手動解析では、値の指定漏れ、不正なオプション、重複指定などを自分で処理する必要があります。
5-3. オプション名と値をDictionaryで管理する
オプションが増える場合は、Dictionary<string, string> に入れると扱いやすくなります。
C#var options = new Dictionary<string, string>();
for (int i = 0; i < args.Length; i++)
{
string key = args[i];
if (key.StartsWith("--"))
{
if (i + 1 < args.Length && !args[i + 1].StartsWith("--"))
{
options[key] = args[i + 1];
i++;
}
else
{
Console.WriteLine($"{key} の値が指定されていません。");
return;
}
}
}
if (options.TryGetValue("--name", out string? name))
{
Console.WriteLine($"名前: {name}");
}
if (options.TryGetValue("--mode", out string? mode))
{
Console.WriteLine($"モード: {mode}");
}
実行例です。
Bashdotnet run -- --name Taro --mode fast
出力例です。
名前: Taro
モード: fast
小さなツールなら、この程度の手動解析でも十分です。ただし、サブコマンドや必須チェック、ヘルプ表示まで実装する場合は、ライブラリの利用を検討しましょう。
5-4. フラグ形式のオプションを扱う
フラグとは、値を持たず、指定されたかどうかだけを見るオプションです。
Bashmyapp.exe --verbose
C#では、次のように判定できます。
C#bool verbose = args.Contains("--verbose");
if (verbose)
{
Console.WriteLine("詳細ログを表示します。");
}
else
{
Console.WriteLine("通常モードで実行します。");
}
実行例です。
Bashdotnet run -- --verbose
出力例です。
詳細ログを表示します。
フラグは、次のような用途でよく使います。
| フラグ | 用途 |
|---|---|
--verbose | 詳細ログを表示する |
--dry-run | 実際には変更せず確認だけする |
--force | 確認なしで実行する |
--help | 使い方を表示する |
5-5. 必須オプションと任意オプションを判定する
実用的なC#コマンドラインアプリでは、「必須オプション」と「任意オプション」を分けて考えます。
たとえば、ファイル処理ツールなら --input は必須、--verbose は任意という設計が考えられます。
C#string? input = null;
bool verbose = false;
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
case "--input":
if (i + 1 >= args.Length)
{
Console.WriteLine("--input の値を指定してください。");
return;
}
input = args[++i];
break;
case "--verbose":
verbose = true;
break;
default:
Console.WriteLine($"不明なオプションです: {args[i]}");
return;
}
}
if (input == null)
{
Console.WriteLine("必須オプション --input を指定してください。");
Console.WriteLine("使い方: myapp --input <ファイルパス> [--verbose]");
return;
}
Console.WriteLine($"入力ファイル: {input}");
if (verbose)
{
Console.WriteLine("詳細ログ: 有効");
}
実行例です。
Bashdotnet run -- --input data.txt --verbose
手動解析では、switch 文を使うと読みやすくなります。
6. System.CommandLineを使った本格的なオプション解析
6-1. System.CommandLineとは
System.CommandLine は、C#で本格的なコマンドラインアプリを作るためのライブラリです。コマンドライン入力の解析、ヘルプ表示、オプション定義など、CLIツールでよく必要になる機能を提供します。Microsoft Learnでも、System.CommandLine はコマンドライン入力の解析やヘルプテキストの表示など、コマンドラインアプリに必要な機能を提供するライブラリと説明されています。
手動解析では次のような処理を自分で書く必要があります。
--name の値を読む
--help を表示する
不正なオプションを検出する
必須オプションをチェックする
型変換する
サブコマンドを処理する
System.CommandLine を使うと、これらを整理して実装できます。
6-2. System.CommandLineの導入方法
プロジェクトに System.CommandLine を追加します。
Bashdotnet add package System.CommandLine
.NET 10以降の環境では、次の形式も使えます。
Bashdotnet package add System.CommandLine
Microsoft Learnのチュートリアルでも、System.CommandLine パッケージを追加する手順としてこれらのコマンドが紹介されています。
導入後、C#コードで次の名前空間を使います。
C#using System.CommandLine;
6-3. Optionを定義して値を受け取る
System.CommandLine では、Option<T> を使ってオプションを定義します。
C#using System.CommandLine;
Option<string> nameOption = new("--name")
{
Description = "表示する名前"
};
RootCommand rootCommand = new("名前を表示するサンプル");
rootCommand.Options.Add(nameOption);
rootCommand.SetAction(parseResult =>
{
string? name = parseResult.GetValue(nameOption);
if (string.IsNullOrWhiteSpace(name))
{
Console.WriteLine("--name を指定してください。");
return 1;
}
Console.WriteLine($"こんにちは、{name}さん!");
return 0;
});
return rootCommand.Parse(args).Invoke();
実行例です。
Bashdotnet run -- --name Taro
出力例です。
こんにちは、Taroさん!
Option<int> を使えば、数値として受け取ることもできます。
C#Option<int> countOption = new("--count")
{
Description = "繰り返し回数"
};
手動で int.Parse しなくても、ライブラリ側で型に応じた解析をしてくれる点が便利です。
6-4. help表示を自動生成する
System.CommandLine の大きなメリットは、ヘルプ表示を自動生成できることです。
たとえば、次のように実行します。
Bashdotnet run -- --help
すると、定義したコマンドやオプションに基づいて、使い方が表示されます。Microsoft Learnのチュートリアルでも、RootCommand は既定でヘルプオプションやバージョンオプションを提供し、--help による表示ができることが示されています。
ヘルプ表示は、ユーザーにとって非常に重要です。特に配布するコマンドラインツールでは、次の情報があると使いやすくなります。
| 項目 | 例 |
|---|---|
| 概要 | 何をするツールか |
| 使い方 | myapp --file input.txt |
| オプション一覧 | --file、--mode |
| 必須か任意か | --file は必須 |
| 具体例 | 実行コマンドのサンプル |
自分で --help を実装することもできますが、オプションが増えるとメンテナンスが大変です。System.CommandLine を使えば、コードで定義した内容をもとにヘルプを生成できるため、実装と説明のズレを減らせます。
6-5. 複雑なコマンドラインツールを作るときの設計ポイント
複雑なC#コマンドラインツールでは、最初にコマンド構造を決めることが大切です。
たとえば、TODO管理ツールなら次のような構造が考えられます。
Bashtodo add "牛乳を買う"
todo list
todo done 1
todo delete 1
この場合、add、list、done、delete はサブコマンドです。
設計するときは、次の点を意識しましょう。
| ポイント | 内容 |
|---|---|
| 1コマンド1役割 | add は追加、delete は削除 |
| 必須値を明確にする | ファイルパスやIDなど |
| オプション名をわかりやすくする | --input、--output |
| 短縮形は必要なものだけ | -i、-o など |
| ヘルプを必ず用意する | --help で確認できるようにする |
| 終了コードを返す | 成功は 0、失敗は 1 など |
最初から大きな設計にする必要はありません。初心者は、まず args で単純な引数を扱い、次に手動で --name のようなオプションを解析し、必要になったら System.CommandLine を導入する流れがおすすめです。
7. エラー処理と入力チェックの実装方法
7-1. 引数が足りない場合の処理
コマンドラインアプリでは、引数不足への対応が必須です。
悪い例です。
C#string input = args[0];
string output = args[1];
引数が足りないと、実行時エラーになります。
良い例です。
C#if (args.Length < 2)
{
Console.WriteLine("使い方: myapp <入力ファイル> <出力ファイル>");
return;
}
string input = args[0];
string output = args[1];
ユーザーが間違えたときに、ただエラーを出すのではなく、正しい使い方を表示するのが親切です。
使い方: myapp <入力ファイル> <出力ファイル>
例: myapp input.txt output.txt
7-2. 数値変換に失敗した場合の処理
数値引数は int.TryParse を使って安全に変換します。
C#if (args.Length == 0)
{
Console.WriteLine("数値を指定してください。");
return;
}
if (!int.TryParse(args[0], out int number))
{
Console.WriteLine("数値として解釈できません。例: myapp 123");
return;
}
Console.WriteLine($"2倍の値: {number * 2}");
実行例です。
Bashdotnet run -- abc
出力例です。
数値として解釈できません。例: myapp 123
例外をそのまま表示すると、初心者ユーザーには原因がわかりにくくなります。TryParse を使って、わかりやすいメッセージを出しましょう。
7-3. 存在しないファイルパスが指定された場合の処理
ファイルパスを受け取る場合は、File.Exists で存在確認します。
C#if (args.Length == 0)
{
Console.WriteLine("読み込むファイルを指定してください。");
return;
}
string path = args[0];
if (!File.Exists(path))
{
Console.WriteLine($"指定されたファイルが存在しません: {path}");
return;
}
string content = File.ReadAllText(path);
Console.WriteLine(content);
さらに実用的にするなら、try-catch で読み込み時の例外にも対応します。
C#try
{
string content = File.ReadAllText(path);
Console.WriteLine(content);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("ファイルを読み取る権限がありません。");
}
catch (IOException ex)
{
Console.WriteLine($"ファイルの読み込み中にエラーが発生しました: {ex.Message}");
}
存在確認だけではなく、権限、ロック、文字コードなどで読み込みに失敗する可能性もあります。
7-4. 不正なオプションを検出する方法
手動解析では、知らないオプションを検出する処理を入れましょう。
C#for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
case "--name":
i++;
break;
case "--verbose":
break;
default:
Console.WriteLine($"不明なオプションです: {args[i]}");
Console.WriteLine("使い方: myapp --name <名前> [--verbose]");
return;
}
}
不正なオプションを無視すると、ユーザーは指定が効いていると誤解する可能性があります。
Bashmyapp.exe --nmae Taro
このように --name を --nmae と打ち間違えた場合、エラーとして知らせるほうが安全です。
7-5. ユーザーにわかりやすいエラーメッセージを書く
良いエラーメッセージには、次の3つが含まれます。
| 要素 | 例 |
|---|---|
| 何が問題か | --input が指定されていません |
| どう直すか | --input <ファイルパス> を指定してください |
| 具体例 | 例: myapp --input data.txt |
悪い例です。
Error
良い例です。
必須オプション --input が指定されていません。
使い方: myapp --input <ファイルパス>
例: myapp --input data.txt
コマンドラインツールは画面がない分、メッセージのわかりやすさが重要です。初心者向け、社内向け、配布向けのどれであっても、エラーメッセージは丁寧に書きましょう。
8. 実用例で学ぶC#コマンドラインアプリの作り方
8-1. 引数で名前を受け取って表示するアプリ
最初の実用例として、名前を受け取って挨拶するアプリを作ります。
C#if (args.Length == 0)
{
Console.WriteLine("使い方: greet <名前>");
return;
}
string name = args[0];
Console.WriteLine($"こんにちは、{name}さん!");
実行例です。
Bashdotnet run -- Hanako
出力例です。
こんにちは、Hanakoさん!
オプション形式にするなら、次のようにできます。
C#string? name = null;
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "--name" && i + 1 < args.Length)
{
name = args[++i];
}
}
if (name == null)
{
Console.WriteLine("使い方: greet --name <名前>");
return;
}
Console.WriteLine($"こんにちは、{name}さん!");
実行例です。
Bashdotnet run -- --name Hanako
8-2. ファイルを読み込むコマンドラインアプリ
次は、指定されたファイルを読み込んで表示するアプリです。
C#if (args.Length == 0)
{
Console.WriteLine("使い方: readfile <ファイルパス>");
return;
}
string path = args[0];
if (!File.Exists(path))
{
Console.WriteLine($"ファイルが見つかりません: {path}");
return;
}
foreach (string line in File.ReadLines(path))
{
Console.WriteLine(line);
}
実行例です。
Bashdotnet run -- sample.txt
ファイルが大きい場合は、File.ReadAllText よりも File.ReadLines のほうが、1行ずつ処理できるため扱いやすい場合があります。
8-3. オプションで処理モードを切り替えるアプリ
--mode オプションで処理を切り替えるサンプルです。
C#string mode = "normal";
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "--mode" && i + 1 < args.Length)
{
mode = args[++i];
}
}
switch (mode)
{
case "normal":
Console.WriteLine("通常モードで実行します。");
break;
case "fast":
Console.WriteLine("高速モードで実行します。");
break;
case "safe":
Console.WriteLine("安全モードで実行します。");
break;
default:
Console.WriteLine($"不明なモードです: {mode}");
Console.WriteLine("指定できる値: normal, fast, safe");
break;
}
実行例です。
Bashdotnet run -- --mode fast
出力例です。
高速モードで実行します。
オプションで処理モードを切り替える設計は、ファイル変換、データ集計、ログ出力、テスト実行などでよく使います。
8-4. ログ出力や終了コードを設定する
コマンドラインアプリでは、終了コードも重要です。一般的には、成功時に 0、失敗時に 0 以外を返します。
Main メソッドを int 戻り値にすると、終了コードを返せます。
C#class Program
{
static int Main(string[] args)
{
if (args.Length == 0)
{
Console.Error.WriteLine("引数が指定されていません。");
return 1;
}
Console.WriteLine("処理に成功しました。");
return 0;
}
}
通常のメッセージは Console.WriteLine、エラーは Console.Error.WriteLine に出すと、標準出力と標準エラー出力を分けられます。
C#Console.WriteLine("通常ログです。");
Console.Error.WriteLine("エラーログです。");
自動化スクリプトやCI/CDで使うC#コマンドラインツールでは、終了コードが特に重要です。呼び出し元が成功・失敗を判定できるようにしましょう。
8-5. 配布用にビルド・発行する方法
開発中は dotnet run で十分ですが、配布する場合は dotnet publish を使います。
Bashdotnet publish -c Release
出力先には、実行に必要なファイルが生成されます。
特定のフォルダに出力するなら、次のようにします。
Bashdotnet publish -c Release -o publish
実行例です。
Bashpublish\CommandLineSample.exe input.txt
環境に依存しない形で配布したい場合は、自己完結型の発行を検討します。
Bashdotnet publish -c Release -r win-x64 --self-contained true -o publish
ただし、自己完結型にすると配布サイズが大きくなります。社内ツールなら、実行先に.NETランタイムを入れる方式でも十分な場合があります。
9. C#コマンドライン実行でよくあるトラブル
9-1. argsが空になる原因
args が空になる原因として多いのは、引数を渡していないことです。
Bashdotnet run
この場合、args.Length は 0 です。
引数を渡すには、次のようにします。
Bashdotnet run -- apple
Visual Studioでデバッグしている場合は、プロジェクトのデバッグ設定にコマンドライン引数を入力しているか確認しましょう。
また、トップレベルステートメントで args を使っているつもりでも、実行時に引数を渡していなければ空になります。まずは次のコードで確認するのがおすすめです。
C#Console.WriteLine(args.Length);
9-2. dotnet runで引数が渡らない原因
dotnet run で引数が渡らない場合、-- の位置を確認してください。
誤解しやすい例です。
Bashdotnet run --configuration Release Taro
この場合、Taro が意図どおりアプリに渡らない可能性があります。
アプリに渡す引数は、次のように -- の後ろに書きます。
Bashdotnet run --configuration Release -- Taro
dotnet run のオプションと、C#アプリ側の引数を区切るために -- を使う、と覚えておきましょう。
9-3. --の使い方を間違えているケース
-- は、それ自体がC#アプリに渡したい値ではなく、「ここから後ろはアプリの引数」という区切りです。
Bashdotnet run -- --name Taro
この場合、C#アプリ側の args は次のようになります。
args[0] = --name
args[1] = Taro
-- 自体は通常、args には入りません。
一方で、ビルド済みのexeを直接実行する場合は、dotnet run の区切りとしての -- は不要です。
Bashmyapp.exe --name Taro
開発中の dotnet run と、配布後のexe直接実行では、コマンドの書き方が少し違う点に注意しましょう。
9-4. スペース入り文字列やパスが分割されるケース
スペース入りの文字列やパスが分割される場合は、引用符で囲みます。
悪い例です。
Bashdotnet run -- C:\My Files\input.txt
分割される可能性があります。
良い例です。
Bashdotnet run -- "C:\My Files\input.txt"
PowerShell、コマンドプロンプト、Bashでは引用符やエスケープの扱いが異なることがあります。まずは args の中身を表示して、実際にどのように渡されているか確認すると原因を見つけやすいです。
C#for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"args[{i}] = {args[i]}");
}
9-5. 文字化けや日本語入力で困ったときの対処法
日本語をコマンドラインで扱う場合、文字化けすることがあります。特にWindowsの古いコンソール環境では、文字コードの影響を受けることがあります。
C#側でUTF-8を指定する例です。
C#using System.Text;
Console.OutputEncoding = Encoding.UTF8;
Console.InputEncoding = Encoding.UTF8;
Console.WriteLine("日本語の表示テスト");
Windowsのコマンドプロンプトでは、次のコマンドでUTF-8に切り替える方法もあります。
Bashchcp 65001
ただし、利用しているターミナルやフォントによって表示結果が変わることがあります。Windows Terminal、PowerShell、Visual Studio Codeのターミナルなど、比較的新しい環境を使うと日本語が扱いやすい場合があります。
10. C#コマンドラインに関するよくある質問
10-1. C#でコマンドライン引数はいくつまで渡せる?
C#の string[] args 自体に、実用上すぐ問題になるような小さな固定上限があるわけではありません。ただし、実際にはOSやシェルのコマンドライン長の制限を受けます。
大量のデータを渡したい場合は、引数にすべて詰め込むのではなく、次の方法を検討しましょう。
| 方法 | 向いているケース |
|---|---|
| ファイルパスを渡す | 大量データを処理する |
| 設定ファイルを読む | 複雑な設定を渡す |
| 標準入力を使う | パイプ処理をする |
| 環境変数を使う | 秘密情報や環境別設定を渡す |
たとえば、大量の文字列を引数に並べるより、input.txt を渡してC#側でファイルを読むほうが実用的です。
10-2. Mainメソッドなしでも引数は受け取れる?
はい。トップレベルステートメントを使えば、明示的な Main メソッドを書かなくても args を使えます。
C#Console.WriteLine($"引数の数: {args.Length}");
foreach (var arg in args)
{
Console.WriteLine(arg);
}
実行例です。
Bashdotnet run -- hello world
ただし、学習の最初は static void Main(string[] args) の形を理解しておくと、C#のエントリポイントやコマンドライン引数の仕組みがわかりやすくなります。
10-3. コマンドライン引数と標準入力の違いは?
コマンドライン引数は、プログラム起動時に渡す値です。
Bashmyapp.exe input.txt
標準入力は、プログラム実行中に外部から流し込む入力です。
Bashtype input.txt | myapp.exe
C#で標準入力を読む例です。
C#string? line;
while ((line = Console.ReadLine()) != null)
{
Console.WriteLine($"受け取った行: {line}");
}
使い分けは次のように考えるとよいでしょう。
| 方式 | 向いている用途 |
|---|---|
| コマンドライン引数 | ファイル名、モード、オプション指定 |
| 標準入力 | 複数行テキスト、パイプ処理、大量データ |
たとえば、処理対象のファイル名は引数で渡し、ファイルの中身は標準入力で渡す、という設計もできます。
10-4. オプション解析ライブラリは使うべき?
小さな学習用プログラムなら、まずは args を直接扱う方法で十分です。
C#string name = args[0];
少し複雑になったら、switch 文で手動解析しても構いません。
C#switch (args[i])
{
case "--name":
name = args[++i];
break;
}
ただし、次の条件に当てはまるなら、System.CommandLine などのライブラリを検討する価値があります。
| 条件 | 理由 |
|---|---|
| オプションが多い | 手動解析が複雑になる |
--help が必要 | 自動生成できる |
| 型変換が多い | int、bool、FileInfo などを扱いやすい |
| サブコマンドがある | add、delete などを整理できる |
| 配布するツール | 使いやすさと保守性が重要 |
初心者は、まず手動で仕組みを理解し、その後ライブラリに進むのがおすすめです。
10-5. 初心者はどこまで実装できれば十分?
初心者が最初に目指すゴールは、次の5つです。
| 目標 | できること |
|---|---|
args を表示できる | 引数の中身を確認できる |
args.Length を使える | 引数不足を防げる |
| 文字列を受け取れる | 名前やメッセージを扱える |
| 数値に変換できる | 計算処理ができる |
| ファイルパスを扱える | 実用的なツールを作れる |
次のコードが理解できれば、C#コマンドラインの基礎は十分です。
C#if (args.Length < 2)
{
Console.WriteLine("使い方: myapp <数値1> <数値2>");
return;
}
if (!int.TryParse(args[0], out int a) ||
!int.TryParse(args[1], out int b))
{
Console.WriteLine("数値を指定してください。");
return;
}
Console.WriteLine($"合計: {a + b}");
その次のステップとして、--input、--output、--verbose のようなオプションを扱えるようになると、実用的なC#コマンドラインアプリを作れるようになります。
まとめ
C#でコマンドライン引数を扱う基本は、string[] args を理解することです。args[0]、args[1] のように順番で値を取り出し、args.Length で引数の数を確認すれば、シンプルなコンソールアプリを作れます。
開発中に dotnet run で引数を渡す場合は、次の形を覚えておきましょう。
Bashdotnet run -- 引数1 引数2
--name や --file のようなオプションを扱う場合は、最初は手動解析でも十分です。オプションが増えたり、--help、サブコマンド、型変換、必須チェックが必要になったりしたら、System.CommandLine の導入を検討するとよいでしょう。
C#コマンドラインアプリを作るうえで大切なのは、次のポイントです。
| ポイント | 内容 |
|---|---|
| 引数の有無を確認する | args.Length を使う |
| 数値変換は安全に行う | TryParse を使う |
| パスは存在確認する | File.Exists を使う |
| スペース入り文字列は引用符で囲む | "Tokyo Station" |
| エラー時は使い方を表示する | ユーザーが直せるメッセージにする |
| 複雑ならライブラリを使う | System.CommandLine を検討する |
まずは、名前を受け取って表示する小さなアプリから始めてみましょう。そこから、数値計算、ファイル読み込み、オプション解析へ進めば、C#で実用的なコマンドラインツールを作れるようになります。

