다음을 통해 공유


사용 안내: System.CommandLine 시작하기

중요합니다

System.CommandLine 는 현재 미리 보기로 제공되며, 이 설명서는 버전 2.0 베타 5용입니다. 일부 정보는 릴리스되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적이거나 묵시적인 보증도 하지 않습니다.

이 자습서에서는 System.CommandLine 라이브러리사용하는 .NET 명령줄 앱을 만드는 방법을 보여줍니다. 먼저 하나의 옵션이 있는 간단한 루트 명령을 만듭니다. 그런 다음, 해당 기반으로 빌드하여 여러 하위 명령과 각 명령에 대한 다양한 옵션을 포함하는 더 복잡한 앱을 만듭니다.

이 자습서에서는 다음 방법을 알아봅니다.

  • 명령, 옵션 및 인수를 만듭니다.
  • 옵션의 기본값을 지정합니다.
  • 명령에 옵션 및 인수를 할당합니다.
  • 명령의 모든 하위 명령에 옵션을 재귀적으로 할당합니다.
  • 여러 수준의 중첩된 하위 명령을 사용합니다.
  • 명령 및 옵션에 대한 별칭을 만듭니다.
  • string, string[], int, bool, FileInfo 및 열거형 옵션 형식과 작업합니다.
  • 명령 작업 코드에서 옵션 값을 읽습니다.
  • 옵션 구문 분석 및 유효성 검사에 사용자 지정 코드를 사용합니다.

필수 조건

또는

앱 만들기

"scl"이라는 .NET 9 콘솔 앱 프로젝트를 만듭니다.

  1. 프로젝트에 대한 scl 폴더를 만든 다음 새 폴더에서 명령 프롬프트를 엽니다.

  2. 다음 명령을 실행합니다.

    dotnet new console --framework net9.0
    

System.CommandLine 패키지 설치

  • 다음 명령을 실행합니다.

    dotnet add package System.CommandLine --prerelease
    

    또는 .NET 10 이상에서 다음을 수행합니다.

    dotnet package add System.CommandLine --prerelease
    

    라이브러리가 베타 버전이므로 --prerelease 옵션이 필요합니다.

인수를 파싱합니다

  1. Program.cs의 내용을 다음 코드로 바꿉니다.

    using System.CommandLine;
    using System.CommandLine.Parsing;
    
    namespace scl;
    
    class Program
    {
        static int Main(string[] args)
        {
            Option<FileInfo> fileOption = new("--file")
            {
                Description = "The file to read and display on the console."
            };
    
            RootCommand rootCommand = new("Sample app for System.CommandLine");
            rootCommand.Options.Add(fileOption);
    
            ParseResult parseResult = rootCommand.Parse(args);
            if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
            {
                ReadFile(parsedFile);
                return 0;
            }
            foreach (ParseError parseError in parseResult.Errors)
            {
                Console.Error.WriteLine(parseError.Message);
            }
            return 1;
        }
    
        static void ReadFile(FileInfo file)
        {
            foreach (string line in File.ReadLines(file.FullName))
            {
                Console.WriteLine(line);
            }
        }
    }
    

앞의 코드는 다음과 같습니다.

Option<FileInfo> fileOption = new("--file")
{
    Description = "The file to read and display on the console."
};

RootCommand rootCommand = new("Sample app for System.CommandLine");
rootCommand.Options.Add(fileOption);
  • args을 구문 분석하고 --file 옵션에 값이 제공되었는지 확인합니다. 이 경우, 구문 분석된 값을 사용하여 ReadFile 메서드를 호출하고 0 종료 코드를 반환합니다.
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
    ReadFile(parsedFile);
    return 0;
}
  • --file 값이 제공되지 않은 경우 사용 가능한 구문 분석 오류를 출력하고 1 종료 코드를 반환합니다.
foreach (ParseError parseError in parseResult.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
return 1;
  • 메서드는 ReadFile 지정된 파일을 읽고 콘솔에 해당 내용을 표시합니다.
static void ReadFile(FileInfo file)
{
    foreach (string line in File.ReadLines(file.FullName))
    {
        Console.WriteLine(line);
    }
}

앱 테스트

명령줄 앱을 개발하는 동안 다음 방법 중 원하는 방법으로 테스트할 수 있습니다.

  • 명령을 실행한 dotnet build 다음 scl/bin/Debug/net9.0 폴더에서 명령 프롬프트를 열어 실행 파일을 실행합니다.

    dotnet build
    cd bin/Debug/net9.0
    scl --file scl.runtimeconfig.json
    
  • 다음 예제와 같이 dotnet run 사용하여 run-- 명령 대신 앱에 옵션 값을 전달합니다.

    dotnet run -- --file bin/Debug/net9.0/scl.runtimeconfig.json
    

이 자습서에서는 이러한 옵션 중 첫 번째 옵션을 사용 중이라고 가정합니다.

앱을 실행하면 --file 옵션으로 지정된 파일의 내용이 표시됩니다.

{
  "runtimeOptions": {
    "tfm": "net9.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "9.0.0"
    }
  }
}

그렇다면 --help을 제공하여 도움말을 표시하도록 요청하면 어떻게 될까요? --file가 제공되지 않고 구문 분석 오류가 없는 시나리오를 앱이 아직 처리하지 않기 때문에 콘솔에 아무것도 출력되지 않습니다.

인수 구문 분석 및 ParseResult 호출

System.CommandLine 를 사용하면 지정된 기호(명령, 지시문 또는 옵션)가 성공적으로 구문 분석될 때 호출되는 작업을 지정할 수 있습니다. 작업은 매개 변수를 System.CommandLine.ParseResult 사용하고 종료 코드를 반환하는 int 대리자입니다(비동기 작업도 사용할 수 있음). 종료 코드는 메서드에서 System.CommandLine.Parsing.ParseResult.Invoke 반환되며 명령이 성공적으로 실행되었는지 여부를 나타내는 데 사용할 수 있습니다.

  1. Program.cs의 내용을 다음 코드로 바꿉니다.

    using System.CommandLine;
    
    namespace scl;
    
    class Program
    {
        static int Main(string[] args)
        {
            Option<FileInfo> fileOption = new("--file")
            {
                Description = "The file to read and display on the console."
            };
    
            RootCommand rootCommand = new("Sample app for System.CommandLine");
            rootCommand.Options.Add(fileOption);
    
            rootCommand.SetAction(parseResult =>
            {
                FileInfo parsedFile = parseResult.GetValue(fileOption);
                ReadFile(parsedFile);
                return 0;
            });
    
            ParseResult parseResult = rootCommand.Parse(args);
            return parseResult.Invoke();
        }
    
        static void ReadFile(FileInfo file)
        {
            foreach (string line in File.ReadLines(file.FullName))
            {
                Console.WriteLine(line);
            }
        }
    }
    

앞의 코드는 다음과 같습니다.

  • 루트 명령이 ReadFile 호출될 때 호출되는 메서드를 지정합니다.

    rootCommand.SetAction(parseResult =>
    {
        FileInfo parsedFile = parseResult.GetValue(fileOption);
        ReadFile(parsedFile);
        return 0;
    });
    
  • args 결과를 구문 분석하고 호출합니다.

    ParseResult parseResult = rootCommand.Parse(args);
    return parseResult.Invoke();
    

앱을 실행하면 --file 옵션으로 지정된 파일의 내용이 표시됩니다.

--help을 제공하여 도움말을 표시하라고 요청하면 어떻게 되나요?

scl --help

다음 출력이 출력됩니다.

Description:
  Sample app for System.CommandLine

Usage:
  scl [options]

Options:
  -?, -h, --help  Show help and usage information
  --version       Show version information
  --file          The file to read and display on the conso

System.CommandLine.RootCommand 기본적으로 도움말 옵션, 버전 옵션Suggest 지시문을 제공합니다. ParseResult.Invoke 메서드는 구문 분석된 기호의 동작을 호출합니다. 우리 명령에 대해 명시적으로 정의된 작업 또는 System.CommandLineSystem.CommandLine.Help.HelpOption에 대해 정의한 도움말 작업일 수 있습니다. 감지된 구문 분석 오류는 표준 오류로 출력되고, 표준 출력에 도움말을 출력하며, 1 종료 코드를 반환합니다.

scl --invalid bla
Unrecognized command or argument '--invalid'.
Unrecognized command or argument 'bla'.

하위 명령 및 옵션 추가

이 섹션에서는 다음을 수행합니다.

  • 추가 옵션을 만듭니다.
  • 하위 명령을 만듭니다.
  • 새 하위 명령에 새 옵션을 할당합니다.

새 옵션을 사용하면 전경 및 배경 텍스트 색과 읽기 속도를 구성할 수 있습니다. 이러한 기능은 Teleprompter 콘솔 앱 자습서인용문 모음을 읽기 위해 사용됩니다.

  1. 이 샘플의 GitHub 리포지토리에서 프로젝트 디렉터리로 sampleQuotes.txt 파일을 복사합니다. 파일을 다운로드하는 방법에 대한 자세한 내용은 샘플 및 자습서지침을 참조하세요.

  2. 프로젝트 파일을 열고 닫는 <ItemGroup> 태그 바로 앞에 </Project> 요소를 추가합니다.

    <ItemGroup>
      <Content Include="sampleQuotes.txt">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </Content>
    </ItemGroup>
    

    이 태그를 추가하면 앱을 빌드할 때 텍스트 파일이 bin/debug/net9.0 폴더에 복사됩니다. 따라서 해당 폴더에서 실행 파일을 실행할 때 폴더 경로를 지정하지 않고 이름으로 파일에 액세스할 수 있습니다.

  3. Program.cs--file 옵션을 만드는 코드 후 읽기 속도 및 텍스트 색을 제어하는 옵션을 만듭니다.

    Option<int> delayOption = new("--delay")
    {
        Description = "Delay between lines, specified as milliseconds per character in a line.",
        DefaultValueFactory = parseResult => 42
    };
    Option<ConsoleColor> fgcolorOption = new("--fgcolor")
    {
        Description = "Foreground color of text displayed on the console.",
        DefaultValueFactory = parseResult => ConsoleColor.White
    };
    Option<bool> lightModeOption = new("--light-mode")
    {
        Description = "Background color of text displayed on the console: default is black, light mode is white."
    };
    
  4. 루트 명령을 만드는 줄 뒤 옵션을 추가하는 --file 코드를 삭제합니다. 새 하위 명령에 추가하므로 여기에서 제거합니다.

  5. 루트 명령을 만드는 줄 뒤 read 하위 명령을 만듭니다. 속성 대신 Options 컬렉션 이니셜라이저 구문을 사용하여 이 하위 명령에 옵션을 추가하고 루트 명령에 하위 명령을 추가합니다.

    Command readCommand = new("read", "Read and display the file.")
    {
        fileOption,
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    rootCommand.Subcommands.Add(readCommand);
    
  6. SetAction 코드를 새 하위 명령에 대한 다음 SetAction 코드로 바꿉다.

    readCommand.SetAction(parseResult => ReadFile(
        parseResult.GetValue(fileOption),
        parseResult.GetValue(delayOption),
        parseResult.GetValue(fgcolorOption),
        parseResult.GetValue(lightModeOption)));
    

    루트 명령에 더 이상 작업이 필요하지 않으므로 더 이상 루트 명령을 호출 SetAction 하지 않습니다. 명령에 하위 명령이 있는 경우 일반적으로 명령줄 앱을 호출할 때 하위 명령 중 하나를 지정해야 합니다.

  7. 작업 메서드를 ReadFile 다음 코드로 바꿉다.

    internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
            Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length));
        }
    }
    

이제 앱은 다음과 같이 표시됩니다.

using System.CommandLine;

namespace scl;

class Program
{
    static int Main(string[] args)
    {
        Option<FileInfo> fileOption = new("--file")
        {
            Description = "The file to read and display on the console."
        };

        Option<int> delayOption = new("--delay")
        {
            Description = "Delay between lines, specified as milliseconds per character in a line.",
            DefaultValueFactory = parseResult => 42
        };
        Option<ConsoleColor> fgcolorOption = new("--fgcolor")
        {
            Description = "Foreground color of text displayed on the console.",
            DefaultValueFactory = parseResult => ConsoleColor.White
        };
        Option<bool> lightModeOption = new("--light-mode")
        {
            Description = "Background color of text displayed on the console: default is black, light mode is white."
        };

        RootCommand rootCommand = new("Sample app for System.CommandLine");

        Command readCommand = new("read", "Read and display the file.")
        {
            fileOption,
            delayOption,
            fgcolorOption,
            lightModeOption
        };
        rootCommand.Subcommands.Add(readCommand);

        readCommand.SetAction(parseResult => ReadFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(delayOption),
            parseResult.GetValue(fgcolorOption),
            parseResult.GetValue(lightModeOption)));

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

    internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
            Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length));
        }
    }
}

새 하위 명령 테스트

이제 하위 명령을 지정하지 않고 앱을 실행하려고 하면 오류 메시지와 사용 가능한 하위 명령을 지정하는 도움말 메시지가 표시됩니다.

scl --file sampleQuotes.txt
'--file' was not matched. Did you mean one of the following?
--help

Required command was not provided.
Unrecognized command or argument '--file'.
Unrecognized command or argument 'sampleQuotes.txt'.

Description:
  Sample app for System.CommandLine

Usage:
  scl [command] [options]

Options:
  -?, -h, --help  Show help and usage information
  --version       Show version information

Commands:
  read  Read and display the file.

하위 명령 read 대한 도움말 텍스트는 네 가지 옵션을 사용할 수 있음을 보여줍니다. 열거형에 유효한 값을 표시합니다.

scl read -h
Description:
  Read and display the file.

Usage:
  scl read [options]

Options:
  --file <file>                                               The file to read and display on the console.
  --delay <delay>                                             Delay between lines, specified as milliseconds per
                                                              character in a line. [default: 42]
  --fgcolor                                                   Foreground color of text displayed on the console.
  <Black|Blue|Cyan|DarkBlue|DarkCyan|DarkGray|DarkGreen|Dark  [default: White]
  Magenta|DarkRed|DarkYellow|Gray|Green|Magenta|Red|White|Ye
  llow>
  --light-mode                                                Background color of text displayed on the console:
                                                              default is black, light mode is white.
  -?, -h, --help                                              Show help and usage information

read 옵션만 지정하는 하위 명령 --file 실행하고 다른 세 가지 옵션에 대한 기본값을 가져옵니다.

scl read --file sampleQuotes.txt

문자당 42밀리초의 기본 지연으로 인해 읽기 속도가 느려집니다. --delay 낮은 숫자로 설정하여 속도를 높일 수 있습니다.

scl read --file sampleQuotes.txt --delay 0

--fgcolor--light-mode 사용하여 텍스트 색을 설정할 수 있습니다.

scl read --file sampleQuotes.txt --fgcolor red --light-mode

--delay 잘못된 값을 입력하면 오류 메시지가 표시됩니다.

scl read --file sampleQuotes.txt --delay forty-two
Cannot parse argument 'forty-two' for option '--int' as expected type 'System.Int32'.

--file 잘못된 값을 입력하면 예외가 발생합니다.

scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException: Could not find file 'C:\bin\Debug\net9.0\nofile''.
File name: 'C:\bin\Debug\net9.0\nofile''

하위 명령 및 사용자 지정 유효성 검사 추가

이 섹션에서는 앱의 최종 버전을 만듭니다. 완료되면 앱에는 다음과 같은 명령과 옵션이 있습니다.

  • 명명된 recursive* 옵션이 있는 루트 명령 --file
    • quotes 명령
      • read, --delay--fgcolor 옵션이 있는 --light-mode 명령
      • addquote 라고 이름이 붙은 인수가 있는 byline 명령
      • 이름이 delete 옵션이 있는 --search-terms 명령

* 재귀 옵션은 할당된 명령에 사용할 수 있으며 모든 하위 명령에 재귀적으로 사용할 수 있습니다.

다음은 옵션 및 인수를 사용하여 사용 가능한 각 명령을 호출하는 샘플 명령줄 입력입니다.

scl quotes read --file sampleQuotes.txt --delay 40 --fgcolor red --light-mode
scl quotes add "Hello world!" "Nancy Davolio"
scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"
  1. Program.cs--file 옵션을 만드는 코드를 다음 코드로 바꿉니다.

    Option<FileInfo> fileOption = new("--file")
    {
        Description = "An option whose argument is parsed as a FileInfo",
        Required = true,
        DefaultValueFactory = result =>
        {
            if (result.Tokens.Count == 0)
            {
                return new FileInfo("sampleQuotes.txt");
    
            }
            string filePath = result.Tokens.Single().Value;
            if (!File.Exists(filePath))
            {
                result.AddError("File does not exist");
                return null;
            }
            else
            {
                return new FileInfo(filePath);
            }
        }
    };
    

    이 코드는 System.CommandLine.Parsing.ArgumentResult 사용하여 사용자 지정 구문 분석, 유효성 검사 및 오류 처리를 제공합니다.

    이 코드가 없으면 누락된 파일이 예외 및 스택 추적으로 보고됩니다. 이 코드를 사용하면 지정된 오류 메시지만 표시됩니다.

    또한 이 코드는 기본값을 지정하므로 사용자 지정 구문 분석 메서드로 설정합니다 DefaultValueFactory .

  2. lightModeOption만드는 코드 후에는 adddelete 명령에 대한 옵션과 인수를 추가합니다.

    Option<string[]> searchTermsOption = new("--search-terms")
    {
        Description = "Strings to search for when deleting entries.",
        Required = true,
        AllowMultipleArgumentsPerToken = true
    };
    Argument<string> quoteArgument = new("quote")
    {
        Description = "Text of quote."
    };
    Argument<string> bylineArgument = new("byline")
    {
        Description = "Byline of quote."
    };
    

    xref:System.CommandLine.Option.AllowMultipleArgumentsPerToken 설정을 사용하면 첫 번째 항목 뒤의 목록에서 요소를 지정할 때 --search-terms 옵션 이름을 생략할 수 있습니다. 명령줄 입력에 해당하는 다음 예제를 만듭니다.

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. 루트 명령과 read 명령을 만드는 코드를 다음 코드로 바꿉다.

    RootCommand rootCommand = new("Sample app for System.CommandLine");
    fileOption.Recursive = true;
    rootCommand.Options.Add(fileOption);
    
    Command quotesCommand = new("quotes", "Work with a file that contains quotes.");
    rootCommand.Subcommands.Add(quotesCommand);
    
    Command readCommand = new("read", "Read and display the file.")
    {
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    quotesCommand.Subcommands.Add(readCommand);
    
    Command deleteCommand = new("delete", "Delete lines from the file.");
    deleteCommand.Options.Add(searchTermsOption);
    quotesCommand.Subcommands.Add(deleteCommand);
    
    Command addCommand = new("add", "Add an entry to the file.");
    addCommand.Arguments.Add(quoteArgument);
    addCommand.Arguments.Add(bylineArgument);
    addCommand.Aliases.Add("insert");
    quotesCommand.Subcommands.Add(addCommand);
    

    이 코드는 다음과 같이 변경합니다.

    • --file 명령에서 read 옵션을 제거합니다.

    • --file 루트 명령에 이 옵션을 재귀 옵션으로 추가합니다.

    • quotes 명령을 만들고 루트 명령에 추가합니다.

    • 루트 명령 대신 read 명령을 quotes 명령에 추가합니다.

    • adddelete 명령을 만들고 quotes 명령에 추가합니다.

    결과는 다음 명령 계층 구조입니다.

    • 루트 명령
      • quotes
        • read
        • add
        • delete

    이제 앱은 부모 명령()이 영역 또는 그룹을 지정하고 자식 명령(quotes, read, add)이 작업인 delete 패턴을 구현합니다.

    재귀 옵션은 명령에 적용되고 하위 명령에 재귀적으로 적용됩니다. --file 루트 명령에 있으므로 앱의 모든 하위 명령에서 자동으로 사용할 수 있습니다.

  4. SetAction 코드 후에 새 하위 명령에 대한 새 SetAction 코드를 추가합니다.

    deleteCommand.SetAction(parseResult => DeleteFromFile(
        parseResult.GetValue(fileOption),
        parseResult.GetValue(searchTermsOption)));
    
    addCommand.SetAction(parseResult => AddToFile(
        parseResult.GetValue(fileOption),
        parseResult.GetValue(quoteArgument),
        parseResult.GetValue(bylineArgument))
        );
    

    하위 명령은 quotes 리프 명령이 아니므로 작업이 없습니다. 하위 명령 read, adddeletequotes아래의 리프 명령이며 각각에 대해 SetAction 호출됩니다.

  5. adddelete에 대한 작업을 추가합니다.

    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");
    
        var lines = File.ReadLines(file.FullName).Where(line => searchTerms.All(s => !line.Contains(s)));
        File.WriteAllLines(file.FullName, lines);
    }
    internal static void AddToFile(FileInfo file, string quote, string byline)
    {
        Console.WriteLine("Adding to file");
    
        using StreamWriter writer = file.AppendText();
        writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}{quote}");
        writer.WriteLine($"{Environment.NewLine}-{byline}");
    }
    

완성된 앱은 다음과 같습니다.

using System.CommandLine;

namespace scl;

class Program
{
    static int Main(string[] args)
    {
        Option<FileInfo> fileOption = new("--file")
        {
            Description = "An option whose argument is parsed as a FileInfo",
            Required = true,
            DefaultValueFactory = result =>
            {
                if (result.Tokens.Count == 0)
                {
                    return new FileInfo("sampleQuotes.txt");

                }
                string filePath = result.Tokens.Single().Value;
                if (!File.Exists(filePath))
                {
                    result.AddError("File does not exist");
                    return null;
                }
                else
                {
                    return new FileInfo(filePath);
                }
            }
        };

        Option<int> delayOption = new("--delay")
        {
            Description = "Delay between lines, specified as milliseconds per character in a line.",
            DefaultValueFactory = parseResult => 42
        };
        Option<ConsoleColor> fgcolorOption = new("--fgcolor")
        {
            Description = "Foreground color of text displayed on the console.",
            DefaultValueFactory = parseResult => ConsoleColor.White
        };
        Option<bool> lightModeOption = new("--light-mode")
        {
            Description = "Background color of text displayed on the console: default is black, light mode is white."
        };

        Option<string[]> searchTermsOption = new("--search-terms")
        {
            Description = "Strings to search for when deleting entries.",
            Required = true,
            AllowMultipleArgumentsPerToken = true
        };
        Argument<string> quoteArgument = new("quote")
        {
            Description = "Text of quote."
        };
        Argument<string> bylineArgument = new("byline")
        {
            Description = "Byline of quote."
        };

        RootCommand rootCommand = new("Sample app for System.CommandLine");
        fileOption.Recursive = true;
        rootCommand.Options.Add(fileOption);

        Command quotesCommand = new("quotes", "Work with a file that contains quotes.");
        rootCommand.Subcommands.Add(quotesCommand);

        Command readCommand = new("read", "Read and display the file.")
        {
            delayOption,
            fgcolorOption,
            lightModeOption
        };
        quotesCommand.Subcommands.Add(readCommand);

        Command deleteCommand = new("delete", "Delete lines from the file.");
        deleteCommand.Options.Add(searchTermsOption);
        quotesCommand.Subcommands.Add(deleteCommand);

        Command addCommand = new("add", "Add an entry to the file.");
        addCommand.Arguments.Add(quoteArgument);
        addCommand.Arguments.Add(bylineArgument);
        addCommand.Aliases.Add("insert");
        quotesCommand.Subcommands.Add(addCommand);

        readCommand.SetAction(parseResult => ReadFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(delayOption),
            parseResult.GetValue(fgcolorOption),
            parseResult.GetValue(lightModeOption)));

        deleteCommand.SetAction(parseResult => DeleteFromFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(searchTermsOption)));

        addCommand.SetAction(parseResult => AddToFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(quoteArgument),
            parseResult.GetValue(bylineArgument))
            );

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

    internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
            Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length));
        }
    }
    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");

        var lines = File.ReadLines(file.FullName).Where(line => searchTerms.All(s => !line.Contains(s)));
        File.WriteAllLines(file.FullName, lines);
    }
    internal static void AddToFile(FileInfo file, string quote, string byline)
    {
        Console.WriteLine("Adding to file");

        using StreamWriter writer = file.AppendText();
        writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}{quote}");
        writer.WriteLine($"{Environment.NewLine}-{byline}");
    }
}

프로젝트를 빌드한 다음, 다음 명령을 시도합니다.

존재하지 않는 파일을 --file 명령을 사용하여 read 제출하면 예외 및 스택 추적 대신 오류 메시지가 표시됩니다.

scl quotes read --file nofile
File does not exist

하위 명령 quotes 실행하면 read, add또는 delete사용하도록 지시하는 메시지가 표시됩니다.

scl quotes
Required command was not provided.

Description:
  Work with a file that contains quotes.

Usage:
  scl quotes [command] [options]

Options:
  --file <file>   An option whose argument is parsed as a FileInfo [default: sampleQuotes.txt]
  -?, -h, --help  Show help and usage information

Commands:
  read                          Read and display the file.
  delete                        Delete lines from the file.
  add, insert <quote> <byline>  Add an entry to the file.

하위 명령 add실행한 다음 텍스트 파일의 끝을 확인하여 추가된 텍스트를 확인합니다.

scl quotes add "Hello world!" "Nancy Davolio"

파일의 시작 부분에서 검색 문자열을 사용하여 하위 명령 delete 실행한 다음 텍스트 파일의 시작을 확인하여 텍스트가 제거된 위치를 확인합니다.

scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"

비고

bin/debug/net9.0 폴더에서 실행하고 있다면, 해당 폴더에서 adddelete 명령어를 통해 변경된 파일을 찾을 수 있습니다. 프로젝트 폴더에 있는 파일의 복사본은 변경되지 않은 상태로 유지됩니다.

다음 단계

이 자습서에서는 System.CommandLine사용하는 간단한 명령줄 앱을 만들었습니다. 라이브러리에 대한 자세한 내용은 System.CommandLine 개요참조하세요.