다음을 통해 공유


명령줄 예측 도구를 만드는 방법

PSReadLine 2.1.0에서는 예측 IntelliSense 기능을 구현하여 스마트 명령줄 예측 도구의 개념을 도입했습니다. PSReadLine 2.2.2에서는 고유한 명령줄 예측 도구를 만들 수 있는 플러그 인 모델을 추가하여 해당 기능을 확장했습니다.

예측 IntelliSense는 입력 시 명령줄에서 제안을 제공하여 탭 완성 기능을 향상합니다. 예측 제안은 커서 뒤에 색이 지정된 텍스트로 표시됩니다. 이렇게 하면 명령 기록 또는 추가 도메인별 플러그 인에서 일치하는 예측을 기반으로 전체 명령을 검색, 편집, 실행할 수 있습니다.

시스템 요구 사항

플러그 인 예측 도구를 생성 및 사용하려면 다음 버전의 소프트웨어를 사용해야 합니다.

  • PowerShell 7.2 이상 - 명령 예측 도구를 만드는 데 필요한 API를 제공합니다.
  • PSReadLine 2.2.2 이상 - 플러그 인을 사용하도록 PSReadLine을 구성할 수 있습니다.

예측 도구 개요

예측 도구는 PowerShell 이진 모듈입니다. 모듈은 System.Management.Automation.Subsystem.Prediction.ICommandPredictor 인터페이스를 구현해야 합니다. 이 인터페이스는 예측 결과를 쿼리하고 피드백을 제공하는 데 사용되는 메서드를 선언합니다.

예측 도구 모듈은 로드 시 PowerShell의 SubsystemManager에서 CommandPredictor 하위 시스템을 등록하고 언로드 시 이 등록을 취소해야 합니다.

다음 다이어그램은 PowerShell의 예측 도구 아키텍처를 보여 줍니다.

아키텍처

코드 만들기

예측 도구를 만들려면 플랫폼에 .NET 6 SDK가 설치되어 있어야 합니다. SDK에 대한 자세한 내용은 .NET 6.0 다운로드를 참조하세요.

다음 단계에 따라 새 PowerShell 모듈 프로젝트를 만듭니다.

  1. dotnet 명령줄 도구를 사용하여 시작 classlib 프로젝트를 만듭니다.

    dotnet new classlib --name SamplePredictor
    
  2. SamplePredictor.csproj를 편집하여 다음 정보를 포함합니다.

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.0" />
      </ItemGroup>
    
    </Project>
    
  3. dotnet에서 만든 기본 Class1.cs 파일을 삭제하고 다음 코드를 프로젝트 폴더의 SamplePredictorClass.cs 파일에 복사합니다.

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Management.Automation;
    using System.Management.Automation.Subsystem;
    using System.Management.Automation.Subsystem.Prediction;
    
    namespace PowerShell.Sample
    {
        public class SamplePredictor : ICommandPredictor
        {
            private readonly Guid _guid;
    
            internal SamplePredictor(string guid)
            {
                _guid = new Guid(guid);
            }
    
            /// <summary>
            /// Gets the unique identifier for a subsystem implementation.
            /// </summary>
            public Guid Id => _guid;
    
            /// <summary>
            /// Gets the name of a subsystem implementation.
            /// </summary>
            public string Name => "SamplePredictor";
    
            /// <summary>
            /// Gets the description of a subsystem implementation.
            /// </summary>
            public string Description => "A sample predictor";
    
            /// <summary>
            /// Get the predictive suggestions. It indicates the start of a suggestion rendering session.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="context">The <see cref="PredictionContext"/> object to be used for prediction.</param>
            /// <param name="cancellationToken">The cancellation token to cancel the prediction.</param>
            /// <returns>An instance of <see cref="SuggestionPackage"/>.</returns>
            public SuggestionPackage GetSuggestion(PredictionClient client, PredictionContext context, CancellationToken cancellationToken)
            {
                string input = context.InputAst.Extent.Text;
                if (string.IsNullOrWhiteSpace(input))
                {
                    return default;
                }
    
                return new SuggestionPackage(new List<PredictiveSuggestion>{
                    new PredictiveSuggestion(string.Concat(input, " HELLO WORLD"))
                });
            }
    
            #region "interface methods for processing feedback"
    
            /// <summary>
            /// Gets a value indicating whether the predictor accepts a specific kind of feedback.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="feedback">A specific type of feedback.</param>
            /// <returns>True or false, to indicate whether the specific feedback is accepted.</returns>
            public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback) => false;
    
            /// <summary>
            /// One or more suggestions provided by the predictor were displayed to the user.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="session">The mini-session where the displayed suggestions came from.</param>
            /// <param name="countOrIndex">
            /// When the value is greater than 0, it's the number of displayed suggestions from the list
            /// returned in <paramref name="session"/>, starting from the index 0. When the value is
            /// less than or equal to 0, it means a single suggestion from the list got displayed, and
            /// the index is the absolute value.
            /// </param>
            public void OnSuggestionDisplayed(PredictionClient client, uint session, int countOrIndex) { }
    
            /// <summary>
            /// The suggestion provided by the predictor was accepted.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="session">Represents the mini-session where the accepted suggestion came from.</param>
            /// <param name="acceptedSuggestion">The accepted suggestion text.</param>
            public void OnSuggestionAccepted(PredictionClient client, uint session, string acceptedSuggestion) { }
    
            /// <summary>
            /// A command line was accepted to execute.
            /// The predictor can start processing early as needed with the latest history.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="history">History command lines provided as references for prediction.</param>
            public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history) { }
    
            /// <summary>
            /// A command line was done execution.
            /// </summary>
            /// <param name="client">Represents the client that initiates the call.</param>
            /// <param name="commandLine">The last accepted command line.</param>
            /// <param name="success">Shows whether the execution was successful.</param>
            public void OnCommandLineExecuted(PredictionClient client, string commandLine, bool success) { }
    
            #endregion;
        }
    
        /// <summary>
        /// Register the predictor on module loading and unregister it on module un-loading.
        /// </summary>
        public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
        {
            private const string Identifier = "843b51d0-55c8-4c1a-8116-f0728d419306";
    
            /// <summary>
            /// Gets called when assembly is loaded.
            /// </summary>
            public void OnImport()
            {
                var predictor = new SamplePredictor(Identifier);
                SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, predictor);
            }
    
            /// <summary>
            /// Gets called when the binary module is unloaded.
            /// </summary>
            public void OnRemove(PSModuleInfo psModuleInfo)
            {
                SubsystemManager.UnregisterSubsystem(SubsystemKind.CommandPredictor, new Guid(Identifier));
            }
        }
    }
    

    다음 예제 코드는 모든 사용자 입력에 대한 예측 결과에 대해 문자열 "HELLO WORLD"를 반환합니다. 샘플 예측 도구는 피드백을 처리하지 않으므로 코드는 인터페이스로부터의 피드백 메서드를 구현하지 않습니다. 예측 및 피드백 코드를 예측 도구의 요구 사항에 맞게 변경합니다.

    참고 항목

    PSReadLine의 목록 보기는 여러 줄 제안을 지원하지 않습니다. 각 제안은 한 줄로 이루어져야 합니다. 코드에 여러 줄 제안이 있는 경우 줄을 별도의 제안으로 분할하거나 세미콜론(;)으로 줄을 결합해야 합니다.

  4. dotnet build를 실행하여 어셈블리를 생성합니다. 컴파일된 어셈블리는 프로젝트 폴더의 bin/Debug/net6.0 위치에서 찾을 수 있습니다.

    참고 항목

    응답성이 뛰어난 사용자 환경을 보장하기 위해 ICommandPredictor 인터페이스는 예측 변수의 응답에 대해 20ms의 시간 초과를 갖습니다. 예측 코드는 20ms 미만의 결과를 반환하여 표시해야 합니다.

예측 도구 플러그 인 사용

새 예측 도구를 사용해 보려면 PowerShell 7.2 세션을 새로 열고 다음 명령을 실행합니다.

Set-PSReadLineOption -PredictionSource HistoryAndPlugin
Import-Module .\bin\Debug\net6.0\SamplePredictor.dll

어셈블리가 세션에 로드되면 터미널에 입력할 때 “HELLO WORLD”라는 텍스트가 표시됩니다. F2 키를 눌러 Inline 보기와 List 보기 간 전환할 수 있습니다.

PSReadLine 옵션에 대한 자세한 내용은 Set-PSReadLineOption을 참조하세요.

다음 명령을 사용하여 설치된 예측 도구 목록을 가져올 수 있습니다.

Get-PSSubsystem -Kind CommandPredictor
Kind              SubsystemType      IsRegistered Implementations
----              -------------      ------------ ---------------
CommandPredictor  ICommandPredictor          True {SamplePredictor}

참고 항목

Get-PSSubsystem은 PowerShell 7.1에 도입된 실험적 cmdlet입니다. 이 cmdlet을 사용하려면 PSSubsystemPluginModel 실험적 기능을 사용하도록 설정해야 합니다. 자세한 내용은 실험적 기능 사용을 참조하세요.