LoginSignup
1
3

More than 3 years have passed since last update.

【メモ】C#でCLIコマンドを作成するためのライブラリ「EntryPoint」の使い方

Last updated at Posted at 2020-11-02

C#でバッチ処理作る機会があり、ベースになるフレームワークがないかと探していたらEntryPointというライブラリを見つけました。
CLIコマンドを保守する上でとても便利なライブラリだと感じました。ですが、日本語の記事が見つからなかったので実装例をメモしておきます。

Nick-Lucas/EntryPoint
https://github.com/Nick-Lucas/EntryPoint

環境

$ dotnet --version
3.1.200

手元にwindowsがなくmacを使用しました。基本的にwindowsでも同じだと思います。
(前はwindwsでビルドしました。)

プロジェクト作成

$ dotnet new console -n QiitaEntryPoint -o QiitaEntryPoint
cd QiitaEntryPoint
dotnet add package EntryPoint

QiitaEntryPointというプロジェクトを作成し、EntryPointをnugetでインストールします。

ディレクトリ構成

$ tree
.
├── CommandLine
│   ├── CliCommands.cs
│   ├── Primary
│   │   ├── PrimaryCliCommand.cs
│   │   └── PrimaryCliCommandArgs.cs
│   └── Secondary
│       └── SecondaryCliCommand.cs
├── Program.cs
└── QiitaEntryPoint.csproj

最終的には上記のようになります。
サンプルコードを参考にしつつ、コマンドの処理が膨らんでもファイルを作りやすいようにコマンドごとにディレクトリを切りました。

実装

今回はPrimaryコマンドとSecondaryコマンドを用意しました。

Primaryコマンド

Primaryコマンドの処理内容となります。
コマンドライン引数と対応したクラス「PrimaryCliCommandArgs」を引数に取ります。

PrimaryCliCommand.cs
using System;

namespace QiitaEntryPoint.CommandLine.Primary
{
    public class PrimaryCliCommand
    {
        public void Handle(PrimaryCliCommandArgs args)
        {
            if (args.AddDay)
            {
                Console.WriteLine($"Hello. {args.Message} by {DateTime.Today.ToShortDateString()}");
            }
            else
            {
                Console.WriteLine(args.Message);
            }
        }
    }
}

こちらはPrimaryコマンドのコマンドライン引数と対応したクラスです。
「BaseCliArguments」を継承します。

PrimaryCliCommandArgs.cs
using EntryPoint;

namespace QiitaEntryPoint.CommandLine.Primary
{
    public class PrimaryCliCommandArgs : BaseCliArguments
    {
        public PrimaryCliCommandArgs() : base("Primary Command") { }

        [Required]
        [OptionParameter(ShortName: 'm',
                         LongName: "message")]
        [Help("Output message")]
        public string Message { get; set; }

        [Option(ShortName: 'd',
                LongName: "day")]
        [Help("add execute day")]
        public bool AddDay { get; set; }
    }
}

下記のコマンドでMessageに対して"I am Engineer"がAddDayに対して"true"が入ってくるイメージです。

$ dotnet run primary -- -m "I am Engineer" -d

[Required]を使用することでその引数は必須になります。

$ dotnet run primary

Arguments Error: 
The option -m/--message was not included, but is a required option

また、LongNameで引数名を指定して、ShortNameで引数の省略形を設定できます。
他にもいろいろ機能があるので以下のサンプルコードを参考にすると良いです。直感的に理解できると思います。

ヘルプを表示するとこんな感じです。

$ dotnet run primary -- --help
Primary Command v1 Documentation

   Usage:
   QiitaEntryPoint [ -o | --option ] [ -p VALUE | --parameter VALUE ] [ operands ]


Arguments:

   -d --day 
   add execute day

   -h --help 
   Displays Help information about arguments when set

   -m --message [String] 
   REQUIRED
   Output message

Secondaryコマンド

Secondaryコマンドは引数を受け取らないシンプルなコマンドとします。

SecondaryCliCommand.cs
using System;

namespace QiitaEntryPoint.CommandLine.Secondary
{
    public class SecondaryCliCommand
    {
        public void Handle()
        {
            Console.WriteLine("Called secondaryCliCommand");
        }
    }
}

処理の振り分け

このクラスは各コマンドのファサードになっており、各コマンドの処理へと振り分けを行います。
「BaseCliCommands」を継承します。

CliCommands.cs
using EntryPoint;
using QiitaEntryPoint.CommandLine.Primary;
using QiitaEntryPoint.CommandLine.Secondary;

namespace QiitaEntryPoint.CommandLine
{
    public class CliCommands : BaseCliCommands
    {
        [DefaultCommand]
        [Command("primary")]
        [Help("The Main command")]
        public void Primary(string[] args)
        {
            var options = Cli.Parse<PrimaryCliCommandArgs>(args);
            var command = new PrimaryCliCommand();
            command.Handle(options);
        }

        [Command("secondary")]
        [Help("The Secondary command")]
        public void Secondary(string[] args)
        {
            var command = new SecondaryCliCommand();
            command.Handle();
        }
    }
}

ここも直感的ですが、CommandAttributeを設定することで、コマンドと処理の紐付けが行われます。
[DefaultCommand]を設定すると、コマンド名を省略したときのデフォルト操作を指定することができます。

Cli.Parse<PrimaryCliCommandArgs>(args);を実行することで、型引数にコマンドライン引数をパースしたインスタンスを取得できます。

ヘルプを表示するとこんな感じです。

$ dotnet run -- --help
Commands for QiitaEntryPoint

Usage:
QiitaEntryPoint [COMMAND] [COMMAND ARGUMENTS]

For Command Help:
QiitaEntryPoint [COMMAND] --help

   PRIMARY [DEFAULT]
   The Main command

   SECONDARY
   The Secondary command

Main関数

最後にエントリーポイントを実装します。

Program.cs
using EntryPoint;
using QiitaEntryPoint.CommandLine;

namespace QiitaEntryPoint
{
    class Program
    {
        static void Main(string[] args)
        {
            Cli.Execute<CliCommands>(args);
        }
    }
}

実行

3つのパターンで実行してみます。

$ dotnet run -- -m "I am Engineer" -d
Hello. I am Engineer by 2020/11/02

$ dotnet run primary -- -m "I am Engineer"
I am Engineer

$ dotnet run secondary
Called secondaryCliCommand

最後に

バッチ処理の実装方法はいろいろあると思いますが、こちらのライブラリを使用することで初見にも優しいソースコードになると思います。(CommandAttributeから追えば良いので)
また、CLIコマンド特有のヘルプ処理などをAttributeで切り出すことにより、本質的な実装に集中できるとともにクリーンなコードを書くことができると感じました。

今回使用したソースコードです。
https://github.com/ishiyama0530/qiita-EntryPoint

1
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3