Öğretici: System.CommandLine ile komut satırı uygulaması oluşturma

Tavsiye

Bu makale, en az bir programlama dili bilen ve C# dilini öğrenen geliştiriciler için yazılan Temel Bilgiler bölümünün bir parçasıdır. Programlamaya yeni başladıysanız Başlarken'i kullanmaya başlayın. Kapsamlı kitaplık kapsamına ihtiyacınız varsa System.CommandLine kitaplık belgelerine bakın.

Kitaplık System.CommandLine , uygulamanızın mantığına odaklanabilmeniz için komut satırı ayrıştırma, yardım metni oluşturma ve giriş doğrulama işlemlerini işler. Bu öğreticide, temel kavramları gösteren bir görev izleyici CLI'sı oluşturacaksınız: komutlar, alt komutlar, seçenekler ve bağımsız değişkenler.

Bu eğitimde, siz:

  • paketini kullanarak System.CommandLine dosya tabanlı bir uygulama oluşturun.
  • Yazılan değerlerle seçenekleri ve bağımsız değişkenleri tanımlayın.
  • Alt komutlar oluşturun ve bunlara seçenekler ve bağımsız değişkenler ekleyin.
  • Her alt komutu bir eylemle işleyin.
  • Uygulamayı farklı komut satırı girişleriyle test edin.

Önkoşullar

Uygulamayı oluşturma

Başlangıç olarak, bir dosya tabanlı C# programı oluşturun ve System.CommandLine paketini ekleyin.

  1. Aşağıdaki içeriğe sahip adlı TaskCli.cs bir dosya oluşturun:

    #!/usr/bin/env dotnet
    

    #! (shebang) satırı, dosyayı doğrudan Unix sistemlerinde çalıştırmanıza olanak tanır. Windows'da dosyayı dotnet run TaskCli.cs çalıştırın.

  2. Gerekli System.CommandLine paket yönergesini ve using deyimlerini ekleyin.

    #:package System.CommandLine@2.0.0
    
    using System.CommandLine;
    using System.CommandLine.Parsing;
    using System.Text.Json;
    

    Önemli

    Sürüm 2.0.0 , yazma sırasındaki en son sürümdür. En son güvenlik düzeltmelerine sahip olduğunuzdan emin olmak için paketin NuGet sayfasında en son sürüme bakın.

Komut yapısını anlama

Herhangi bir ayrıştırma kodu yazmadan önce CLI'nin kullanıcı açısından nasıl göründüğünü göz önünde bulundurun. Görev izleyicisi dört işlemi destekler:

dotnet TaskCli.cs -- add "Write documentation" --priority High --due 2026-04-01
dotnet TaskCli.cs -- list --all
dotnet TaskCli.cs -- complete 3
dotnet TaskCli.cs -- remove 3
dotnet TaskCli.cs -- --verbose list

Uyarı

-- Yukarıdaki örneklerde TaskCli.cs sonrası yer alan dotnet run ifadesi, kalan tüm bağımsız değişkenlerin CLI tarafından yorumlanmak yerine, uygulamanıza iletildiğini bildirirdotnet.

Her satırda birkaç komut satırı kavramı kullanılır:

  • Alt komutlar , uygulamaya ne yapacağını belirten fiillerdir. Görev izleyicisinin dört tane vardır: add, list, completeve remove. Her alt komut kendi parametrelerini tanımlayabilir.
  • Bağımsız değişkenler , bir alt komutu izleyen konumsal değerlerdir. add "Write documentation" içinde, string "Write documentation", görev açıklamasını belirten bir bağımsız değişkendir. complete 3, sayısı 3, görev kimliğini belirten bir argümandır.
  • Seçenekler-- ile öneklenmiş adlandırılmış değerlerdir. add --priority High --due 2026-04-01 içinde, hem --priority hem de --due kendi değerlerine sahip seçeneklerdir. list --all içinde --allseçeneği, değer gerektirmeyen bir Boole bayrağıdır.
  • Genel seçenekler her alt komut için geçerlidir. seçeneği --verbose ile Recursive = truekök komutunda tanımlanır, bu nedenle herhangi bir alt komutla çalışır. --verbose list'da, verbose bayrağı alt komutun önünde görünür, ancak list --verbose de eşit derecede iyi çalışır.

İzleyen bölümlerde bu parçaları aşağıdan yukarıya doğru oluşturursunuz. İlk olarak, tek tek seçenekleri (ve gibi --priority--all) ve bağımsız değişkenleri (görev açıklaması ve kimlik gibi) tanımlarsınız. Ardından, dört alt komutu oluşturur ve her birine ilgili seçenekleri ve bağımsız değişkenleri eklersiniz. Ardından, her alt komut için bir eylem tanımlarsınız. Eylem, kullanıcı bu komutu çağırdığında çalışan koddur. Son olarak kök komutunu derler, girişi ayrıştırıp eşleşen eylemi çağırırsınız.

Komut satırı söz dizimi kavramlarına daha ayrıntılı bir bakış için bkz. Komut satırı söz dizimine genel bakış.

Seçenekleri ve bağımsız değişkenleri tanımlama

Seçenekler, kullanıcıların ön -- ek ile belirttiği adlandırılmış değerleri temsil eder. Bağımsız değişkenler pozisyonel değerleri temsil eder. Her ikisi de güçlü şekilde türlenmiştir. System.CommandLine giriş dizesini belirttiğiniz türe ayrıştırıyor.

Kütüphane, System.CommandLine tür güvenliğini sağlamak için genel türleri kullanır. yazdığınızda Option<int>int köşeli ayraçlar arasındaki değer bir tür bağımsız değişkenidir. Kitaplığa seçeneğin ne tür bir değer içerdiğini bildirir. Sınıfın kendisi bir tür parametresiT (içinde System.CommandLine.Option<T>olduğu gibi) bildirir ve bir örnek oluşturduğunuzda somut türü sağlarsınız. Kitaplık, kullanıcının giriş dizesini ayrıştırarak otomatik olarak bu türe dönüştürür. Kullanıcı bir --delay abcsağlarsaOption<int>, System.CommandLine kodunuza hatalı veri geçirmek yerine ayrıştırma hatası bildirir. Aşağıdaki adımlarda , Option<bool>, Option<Priority>ve Option<DateOnly?> ile System.CommandLine.Argument<T>bu deseni görürsünüz. Genel bilgiler hakkında daha fazla bilgi için bkz . Genel değerler.

  1. Seçenekleri tanımlayın. Her Option<T> bir değer türünü, adı ve açıklamayı belirtir. --priority seçeneği bir enum türü kullanır ve System.CommandLine girdisini, geçerli enum değerlerine göre otomatik olarak doğrular.

    var verboseOption = new Option<bool>("--verbose")
    {
        Description = "Show detailed output",
        Recursive = true
    };
    
    var priorityOption = new Option<Priority>("--priority")
    {
        Description = "Task priority level",
        DefaultValueFactory = _ => Priority.Medium
    };
    
    var dueOption = new Option<DateOnly?>("--due")
    {
        Description = "Due date (uses current culture date format)"
    };
    
    var allOption = new Option<bool>("--all")
    {
        Description = "Include completed tasks"
    };
    

    Recursive = true ayarı, --verbose seçeneğini her alt komutta kullanılabilir hale getirir. --priority üzerindeki DefaultValueFactory, kullanıcıların seçeneği geçersiz kılabilmesi için varsayılan bir değer sağlar.

  2. Bağımsız değişkenleri tanımlayın. Her Argument<T> bir değer türünü ve bir adı belirtir:

    var descriptionArgument = new Argument<string>("description")
    {
        Description = "Task description"
    };
    
    var taskIdArgument = new Argument<int>("id")
    {
        Description = "Task ID"
    };
    

Kurulum komutları ve alt komutları

A System.CommandLine.Command , kullanıcının çağırabileceği bir eylemi temsil eder. Her komut için System.CommandLine hangi parametrelerin nereye ait olduğunu anlaması amacıyla ilgili seçenekleri ve bağımsız değişkenleri ekleyin.

  1. Dört alt komutu oluşturun. Her komut kendi seçenek ve bağımsız değişken bileşimini alır:

    var addCommand = new Command("add", "Add a new task")
    {
        Arguments = { descriptionArgument },
        Options = { priorityOption, dueOption }
    };
    
    var listCommand = new Command("list", "List all tasks")
    {
        Options = { allOption }
    };
    
    var completeCommand = new Command("complete", "Mark a task as complete")
    {
        Arguments = { taskIdArgument }
    };
    
    var removeCommand = new Command("remove", "Remove a task")
    {
        Arguments = { taskIdArgument }
    };
    
  2. Kök komutunu derleyin. System.CommandLine.RootCommand, CLI için giriş noktasıdır. Genel --verbose seçeneği ve tüm alt komutları ekleyin:

    var rootCommand = new RootCommand("A simple task tracker CLI")
    {
        Options = { verboseOption },
        Subcommands = { addCommand, listCommand, completeCommand, removeCommand }
    };
    

    Otomatik oluşturulan yardım metni, kullanıcı tarafından çalıştırıldığında TaskCli --helpkök komut açıklamasını gösterir.

Komutları eylemlerle yönetme

Her alt komutun bir eyleme ihtiyacı vardır. Eylem, kullanıcı bu komutu çağırdığında çalışan bir temsilcidir . Delege, bir yönteme referans veren bir türdür. Burada, temsilci olarak bir lambda ifadesi (bir => ile tanımlanan satır içi anonim işlev) geçirirsiniz. Her eylemi atamak için SetAction çağır. Temsilci, ParseResult aracılığıyla ayrıştırılan değerlere GetValue ile erişim sağlar.

  1. Komutun eylemini add ayarlayın. Bu eylem, dize enterpolasyonu ($"..." küme ayraçlarına ifadeler gömen dizeler), koşullu işlemci (?:) ve null atanabilir bir değerin değeri olup olmadığını denetleyen ve bunu tek adımda yeni bir değişkene atayan bir tür testi deseni (due is DateOnly dueDate) tanıtır.

    addCommand.SetAction(parseResult =>
    {
        var description = parseResult.GetValue(descriptionArgument)!;
        var priority = parseResult.GetValue(priorityOption);
        var due = parseResult.GetValue(dueOption);
        var verbose = parseResult.GetValue(verboseOption);
    
        var tasks = LoadTasks();
        var id = tasks.Count > 0 ? tasks.Max(t => t.Id) + 1 : 1;
        var task = new TaskItem(id, description, priority, due, false);
        tasks.Add(task);
        SaveTasks(tasks);
    
        Console.WriteLine($"Added task {id}: {description}");
        if (verbose)
        {
            Console.WriteLine($"  Priority: {priority}");
            if (due is DateOnly dueDate)
            {
                Console.WriteLine($"  Due: {dueDate}");
            }
        }
    });
    
  2. list komutu için eylemi ayarlayın. LINQ (Dil Tümleşik Sorgu), bellek içi koleksiyonlar için standart sorgu işleçleri sağlar. Bu eylemde, Enumerable.Where görevleri yalnızca bir koşulla eşleşen öğelere filtreler ve Enumerable.ToList filtrelenmiş diziyi bir listeye uygular. Eylem daha sonra sonuçları yinelemek için bir foreach döngü ve durum simgesi seçmek için koşullu işleç kullanır:

    listCommand.SetAction(parseResult =>
    {
        var showAll = parseResult.GetValue(allOption);
        var verbose = parseResult.GetValue(verboseOption);
    
        var tasks = LoadTasks();
        var filtered = showAll ? tasks : tasks.Where(t => !t.IsComplete).ToList();
    
        if (filtered.Count == 0)
        {
            Console.WriteLine("No tasks found.");
            return;
        }
    
        foreach (var task in filtered)
        {
            var status = task.IsComplete ? "✓" : " ";
            Console.WriteLine($"  [{status}] {task.Id}: {task.Description}");
            if (verbose)
            {
                Console.WriteLine($"       Priority: {task.Priority}");
                if (task.Due is DateOnly dueDate)
                {
                    Console.WriteLine($"       Due: {dueDate}");
                }
            }
        }
    });
    

    Daha fazla ayrıntı için bkz. LINQ.

  3. complete komutunun eylemini ayarlayın. Bu eylem, bulmak için LINQ'leri Enumerable.FirstOrDefault kullanır:

    • Eşleştirme görevi.
    • Görevin var olup olmadığını denetlemek için kullanılan bir desen.is null
    • Önce with mevcut değerleri kopyalayıp ardından başlatıcıda with ayarladığınız özellikleri uygulayarak yeni bir kayıt örneği oluşturmaya yönelik ifade (burada, IsComplete = true). Kayıtlar varsayılan olarak sabittir, bu nedenle bu kopyalama ve güncelleştirme düzeni değiştirilmiş bir değeri nasıl oluşturduğunuzdur.

    Eylem başarısız olabileceğinden (örneğin, görev kimliği mevcut olmadığından), eylem uygulamanın çıkış koduna dönüşen bir tamsayı hata kodu döndürür:

    completeCommand.SetAction(parseResult =>
    {
        var id = parseResult.GetValue(taskIdArgument);
        var verbose = parseResult.GetValue(verboseOption);
    
        var tasks = LoadTasks();
        var task = tasks.FirstOrDefault(t => t.Id == id);
    
        if (task is null)
        {
            Console.Error.WriteLine($"Task {id} not found.");
            return -1;
        }
    
        tasks[tasks.IndexOf(task)] = task with { IsComplete = true };
        SaveTasks(tasks);
    
        Console.WriteLine($"Completed task {id}: {task.Description}");
        if (verbose)
        {
            Console.WriteLine($"  Priority: {task.Priority}");
        }
        return 0;
    });
    
  4. remove komutunun eylemini ayarlayın. Bu eylem, complete ile aynı arama ve doğrulama kalıbını izler. Görevi FirstOrDefault bulmak ve eksik durumu işlemek için is null kullanın.

    removeCommand.SetAction(parseResult =>
    {
        var id = parseResult.GetValue(taskIdArgument);
        var verbose = parseResult.GetValue(verboseOption);
    
        var tasks = LoadTasks();
        var task = tasks.FirstOrDefault(t => t.Id == id);
    
        if (task is null)
        {
            Console.Error.WriteLine($"Task {id} not found.");
            return -1;
        }
    
        tasks.Remove(task);
        SaveTasks(tasks);
    
        Console.WriteLine($"Removed task {id}: {task.Description}");
        if (verbose)
        {
            Console.WriteLine($"  Priority: {task.Priority}");
        }
        return 0;
    });
    
  5. Komut satırını ayrıştırın ve eşleşen eylemi çağırın:

    return rootCommand.Parse(args).Invoke();
    

    rootCommand.Parse(args), girişi ParseResult olarak ayrıştırır ve .Invoke(), eşleşen komut için eylemi çalıştırır. Dönüş değeri bir çıkış kodudur (başarı için 0).

Destekleyici türler ve veri yardımcıları ekleme

Uygulamanın birkaç destekleyici parçaya ihtiyacı vardır: yerel işlevler, bir enum, bir recordve bir serileştirme bağlamı. Aşağıdaki bölümlerde her bir kavram tanıtılarak, neden seçeceğiniz açıklanır ve kod gösterilir. Uygulama, görevleri JSON (JavaScript Nesne Gösterimi) olarak depolamak için System.Text.Json kullanır. Dosya tabanlı uygulamalar, tüm üst düzey deyimlerden ve yerel işlevlerden sonra tür bildirimlerinin gösterilmesini gerektirir.

  1. Görevleri yükleyip kaydeden yerel işlevleri ekleyin. Yerel işlev, diğer yerel işlevlerin içinde de dahil olmak üzere başka bir işlev içinde bildirilen bir yöntemdir. Yerel işlevler yardımcı mantığı çağıran koda yakın tutar ve böylece okuyucunun akışı anlamak için ayrı bir sınıfa veya dosyaya atlaması gerekmeyen okunabilirliği artırır. Burada LoadTasks ve SaveTasks, birden fazla komut işleminin paylaştığı dosya G/Ç'sini kapsüller, böylece uygulama yükleme/kaydetme mantığını bir kez yazar ve yeniden kullanır.

    static string GetTaskFilePath() => Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
        "taskcli-sample",
        "tasks.json");
    
    static List<TaskItem> LoadTasks()
    {
        var path = GetTaskFilePath();
        if (!File.Exists(path))
        {
            return [];
        }
    
        var json = File.ReadAllText(path);
        return JsonSerializer.Deserialize(json, TaskJsonContext.Default.ListTaskItem) ?? [];
    }
    
    static void SaveTasks(List<TaskItem> tasks)
    {
        var path = GetTaskFilePath();
        Directory.CreateDirectory(Path.GetDirectoryName(path)!);
        var json = JsonSerializer.Serialize(tasks, TaskJsonContext.Default.ListTaskItem);
        File.WriteAllText(path, json);
    }
    
  2. Dosyanın sonuna Priority enum ve TaskItem record ekleyin:

    public enum Priority { Low, Medium, High }
    

    , enum tamsayı türü tarafından desteklenen sabit bir adlandırılmış sabit kümesini tanımlayan bir değer türüdür. Öncelik düzeylerini düz tamsayılarla (0, 1, 2) temsil edebilirsiniz, ancak birkaç enum nedenden dolayı daha iyi bir seçimdir: derleyici atamaları tanımlı adlarla kısıtlar, bu nedenle gibi Hihg bir yazım hatası sessiz bir hata yerine derleme zamanı hatasına neden olur; adları Low, Mediumve High kodu daha okunabilir hale getirir ve System.CommandLine kullanıcı girişini sabit listesi üyelerine karşı otomatik olarak doğrular, böylece ücretsiz giriş denetimi elde edersiniz.

    public record TaskItem(int Id, string Description, Priority Priority, DateOnly? Due, bool IsComplete);
    

    A record , derleyicinin değer tabanlı eşitlik ve yıkıcı olmayan mutasyon ile donatdığı bir türdür. record TaskItem için en uygun olanıdır, çünkü görev verileri karmaşık bir davranış içermeyen basit bir durumdur—görevleri başvuru kimliğine göre değil, değerlerine göre karşılaştırırsınız. Derleyici, oluşturucu parametrelerinden Equals, GetHashCode, ToString ve with ifade desteği üretir, böylece şablon kod yazmadan doğru eşitlik denetimleri, kolay hata ayıklama çıktısını ve değiştirilemez güncellemeler alırsınız. Kayıtlar varsayılan olarak sabit olduğundan, orijinal öğeyi değiştirmek yerine değiştirilmiş bir kopya (tıpkı complete ifadesinin yaptığı gibi) üretmek için bir with ifade kullanırsınız. Bu, farklı eylemler aynı listeyi okurken ve yazarken yanlışlıkla yan etkileri önler.

  3. AOT uyumlu serileştirme için JSON serileştirme bağlamını ekleyin:

    [System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
    [System.Text.Json.Serialization.JsonSerializable(typeof(List<TaskItem>))]
    internal partial class TaskJsonContext : System.Text.Json.Serialization.JsonSerializerContext;
    

    Bu sınıf iki öznitelik (bir bildirimin üzerindeki köşeli ayraçlara yerleştirilen meta veri ek açıklamaları) kullanır. özniteliği, [JsonSourceGenerationOptions(WriteIndented = true)] kaynak oluşturucuya okunabilirlik için girintili JSON yaymasını söyler. [JsonSerializable(typeof(List<TaskItem>))] özniteliği, oluşturucuya serileştirme kodunun oluşturulacağı türü bildirir. Bu öznitelikler birlikte, çalışma zamanı yansımasını önleyen ve önceden (AOT) derlemeyi destekleyen kaynak tarafından oluşturulan serileştirmeyi etkinleştirir.

Uygulamayı test et

Her alt komutu uygulamak için uygulamayı farklı girişlerle çalıştırın.

  1. Otomatik oluşturulan yardımı görüntüleyin:

    dotnet run TaskCli.cs -- --help
    
    Description:
      A simple task tracker CLI
    
    Usage:
      TaskCli [command] [options]
    
    Options:
      --verbose       Show detailed output
      -?, -h, --help  Show help and usage information
      --version       Show version information
    
    Commands:
      add <description>  Add a new task
      list               List all tasks
      complete <id>      Mark a task as complete
      remove <id>        Remove a task
    
  2. Farklı seçeneklere sahip görevler ekleyin:

    dotnet run TaskCli.cs -- add "Write documentation" --priority High --due 2026-04-01
    dotnet run TaskCli.cs -- add "Review pull request"
    dotnet run TaskCli.cs -- add "Fix build errors"
    
    Added task 1: Write documentation
    Added task 2: Review pull request
    Added task 3: Fix build errors
    
  3. Görevleri listeleyin ve ek ayrıntıları göstermek için kullanın --verbose :

    dotnet run TaskCli.cs -- --verbose list
    
      [ ] 1: Write documentation
           Priority: High
           Due: 4/1/2026
      [ ] 2: Review pull request
           Priority: Medium
      [ ] 3: Fix build errors
           Priority: Medium
    
  4. Bir görevi tamamlayın, ardından listenin tamamlanan görevleri varsayılan olarak filtrelediğinden emin olun:

    dotnet run TaskCli.cs -- complete 2
    dotnet run TaskCli.cs -- list
    
    Completed task 2: Review pull request
      [ ] 1: Write documentation
      [ ] 3: Fix build errors
    
  5. Tamamlanmış görevleri eklemek için kullanın --all :

    dotnet run TaskCli.cs -- list --all
    
      [ ] 1: Write documentation
      [✓] 2: Review pull request
      [ ] 3: Fix build errors
    
  6. Görevi kaldırma:

    dotnet run TaskCli.cs -- remove 3
    
    Removed task 3: Fix build errors
    

Kaynakları temizle

Görev izleyicisi verileri yerel uygulama veri klasörünüz altındaki bir JSON dosyasında depolar. taskcli-sample Örnek verileri kaldırmak için klasörü silin:

  • Windows: silin %LOCALAPPDATA%\taskcli-sample.
  • macOS: silin ~/Library/Application Support/taskcli-sample.
  • Linux: silin ~/.local/share/taskcli-sample.