명령줄 예측 도구를 만드는 방법
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 모듈 프로젝트를 만듭니다.
dotnet
명령줄 도구를 사용하여 시작 classlib 프로젝트를 만듭니다.dotnet new classlib --name SamplePredictor
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>
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의 목록 보기는 여러 줄 제안을 지원하지 않습니다. 각 제안은 한 줄로 이루어져야 합니다. 코드에 여러 줄 제안이 있는 경우 줄을 별도의 제안으로 분할하거나 세미콜론(
;
)으로 줄을 결합해야 합니다.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
실험적 기능을 사용하도록 설정해야 합니다. 자세한 내용은 실험적 기능 사용을 참조하세요.
PowerShell