다음을 통해 공유


데이터 저장소에 액세스하는 Cmdlet 만들기

이 섹션에서는 Windows PowerShell 공급자를 통해 저장된 데이터에 액세스하는 cmdlet을 만드는 방법을 설명합니다. 이 유형의 cmdlet은 Windows PowerShell 런타임의 Windows PowerShell 공급자 인프라를 사용하므로 cmdlet 클래스는 System.Management.Automation.PSCmdlet 기본 클래스에서 파생되어야 합니다.

여기에 설명된 Select-Str cmdlet은 파일 또는 개체에서 문자열을 찾아 선택할 수 있습니다. 문자열을 식별하는 데 사용되는 패턴은 cmdlet의 Path 매개 변수를 통해 명시적으로 지정하거나 Script 매개 변수를 통해 암시적으로 지정할 수 있습니다.

이 cmdlet은 System.Management.Automation.Provider.IContentCmdletProvider파생되는 모든 Windows PowerShell 공급자를 사용하도록 설계되었습니다. 예를 들어 cmdlet은 Windows PowerShell에서 제공하는 FileSystem 공급자 또는 변수 공급자를 지정할 수 있습니다. Windows PowerShell 공급자에 대한 자세한 내용은 windows PowerShell 공급자 디자인하는참조하세요.

Cmdlet 클래스 정의

cmdlet 만들기의 첫 번째 단계는 항상 cmdlet의 이름을 지정하고 cmdlet을 구현하는 .NET 클래스를 선언하는 것입니다. 이 cmdlet은 특정 문자열을 검색하므로 여기서 선택한 동사 이름은 System.Management.Automation.VerbsCommon 클래스로 정의된 "Select"입니다. 명사 이름 "Str"은 cmdlet이 문자열에 작동하기 때문에 사용됩니다. 아래 선언에서 cmdlet 동사와 명사 이름은 cmdlet 클래스의 이름에 반영됩니다. 승인된 cmdlet 동사에 대한 자세한 내용은 Cmdlet 동사 이름참조하세요.

이 cmdlet의 .NET 클래스는 Windows PowerShell 공급자 인프라를 노출하는 데 필요한 지원을 Windows PowerShell 런타임에 제공하므로 기본 클래스를 System.Management.Automation.PSCmdlet에서 파생되어야 합니다. 또한 이 cmdlet은 System.Text.RegularExpressions.Regex같은 .NET Framework 정규식 클래스를 사용합니다.

다음 코드는 이 Select-Str cmdlet에 대한 클래스 정의입니다.

[Cmdlet(VerbsCommon.Select, "Str", DefaultParameterSetName="PatternParameterSet")]
public class SelectStringCommand : PSCmdlet

이 cmdlet은 클래스 선언에 DefaultParameterSetName 특성 키워드를 추가하여 기본 매개 변수 집합을 정의합니다. Script 매개 변수를 지정하지 않은 경우 기본 매개 변수 집합 PatternParameterSet 사용됩니다. 이 매개 변수 집합에 대한 자세한 내용은 다음 섹션에서 PatternScript 매개 변수 토론을 참조하세요.

데이터 액세스에 대한 매개 변수 정의

이 cmdlet은 사용자가 저장된 데이터에 액세스하고 검사할 수 있도록 하는 몇 가지 매개 변수를 정의합니다. 이러한 매개 변수에는 데이터 저장소의 위치를 나타내는 Path 매개 변수, 검색에 사용할 패턴을 지정하는 Pattern 매개 변수 및 검색 수행 방법을 지원하는 다른 여러 매개 변수가 포함됩니다.

비고

매개 변수 정의의 기본 사항에 대한 자세한 내용은 명령줄 입력 처리하는 매개 변수 추가참조하세요.

경로 매개 변수 선언

데이터 저장소를 찾으려면 이 cmdlet은 Windows PowerShell 경로를 사용하여 데이터 저장소에 액세스하도록 설계된 Windows PowerShell 공급자를 식별해야 합니다. 따라서 공급자의 위치를 나타내기 위해 문자열 배열 형식의 Path 매개 변수를 정의합니다.

[Parameter(
           Position = 0,
           ParameterSetName = "ScriptParameterSet",
           Mandatory = true)]
[Parameter(
           Position = 0,
           ParameterSetName = "PatternParameterSet",
           ValueFromPipeline = true,
           Mandatory = true)]
           [Alias("PSPath")]
public string[] Path
{
  get { return paths; }
  set { paths = value; }
}
private string[] paths;

이 매개 변수는 서로 다른 두 매개 변수 집합에 속하며 별칭이 있습니다.

System.Management.Automation.ParameterAttribute 특성은 Path 매개 변수가 ScriptParameterSetPatternParameterSet속한다고 선언합니다. 매개 변수 집합에 대한 자세한 내용은 cmdlet매개 변수 집합 추가를 참조하세요.

System.Management.Automation.AliasAttribute 특성은 Path 매개 변수에 대한 PSPath 별칭을 선언합니다. Windows PowerShell 공급자에 액세스하는 다른 cmdlet과의 일관성을 위해 이 별칭을 선언하는 것이 좋습니다. Windows PowerShell 경로에 대한 자세한 내용은 Windows PowerShell 작동 방식 "PowerShell 경로 개념"을 참조하세요.

패턴 매개 변수 선언

검색할 패턴을 지정하기 위해 이 cmdlet은 문자열 배열인 Pattern 매개 변수를 선언합니다. 데이터 저장소에 패턴이 있으면 양수 결과가 반환됩니다. 이러한 패턴은 컴파일된 정규식 배열 또는 리터럴 검색에 사용되는 와일드카드 패턴 배열로 컴파일할 수 있습니다.

[Parameter(
           Position = 1,
           ParameterSetName = "PatternParameterSet",
           Mandatory = true)]
public string[] Pattern
{
  get { return patterns; }
  set { patterns = value; }
}
private string[] patterns;
private Regex[] regexPattern;
private WildcardPattern[] wildcardPattern;

이 매개 변수를 지정하면 cmdlet은 PatternParameterSet기본 매개 변수 집합을 사용합니다. 이 경우 cmdlet은 여기에 지정된 패턴을 사용하여 문자열을 선택합니다. 반면, Script 매개 변수를 사용하여 패턴이 포함된 스크립트를 제공할 수도 있습니다. ScriptPattern 매개 변수는 두 개의 개별 매개 변수 집합을 정의하므로 상호 배타적입니다.

검색 지원 매개 변수 선언

이 cmdlet은 cmdlet의 검색 기능을 수정하는 데 사용할 수 있는 다음 지원 매개 변수를 정의합니다.

Script 매개 변수는 cmdlet에 대한 대체 검색 메커니즘을 제공하는 데 사용할 수 있는 스크립트 블록을 지정합니다. 스크립트는 일치에 사용되는 패턴을 포함하고 System.Management.Automation.PSObject 개체를 반환해야 합니다. 이 매개 변수는 ScriptParameterSet 매개 변수 집합을 식별하는 고유 매개 변수이기도 합니다. Windows PowerShell 런타임에 이 매개 변수가 표시되면 ScriptParameterSet 매개 변수 집합에 속하는 매개 변수만 사용합니다.

[Parameter(
           Position = 1,
           ParameterSetName = "ScriptParameterSet",
           Mandatory = true)]
public ScriptBlock Script
{
  set { script = value; }
  get { return script; }
}
ScriptBlock script;

SimpleMatch 매개 변수는 cmdlet이 제공된 패턴과 명시적으로 일치하는지 여부를 나타내는 스위치 매개 변수입니다. 사용자가 명령줄(true)에서 매개 변수를 지정하면 cmdlet은 제공된 패턴을 사용합니다. 매개 변수가 지정되지 않은 경우(false) cmdlet은 정규식을 사용합니다. 이 매개 변수의 기본값은 false.

[Parameter]
public SwitchParameter SimpleMatch
{
  get { return simpleMatch; }
  set { simpleMatch = value; }
}
private bool simpleMatch;

CaseSensitive 매개 변수는 대/소문자 구분 검색이 수행되는지 여부를 나타내는 switch 매개 변수입니다. 사용자가 명령줄(true)에서 매개 변수를 지정하면 cmdlet은 패턴을 비교할 때 문자의 대문자와 소문자를 확인합니다. 매개 변수가 지정되지 않은 경우(false), cmdlet은 대문자와 소문자를 구분하지 않습니다. 예를 들어 "MyFile" 및 "myfile"은 모두 양의 적 중 하나로 반환됩니다. 이 매개 변수의 기본값은 false.

[Parameter]
public SwitchParameter CaseSensitive
{
  get { return caseSensitive; }
  set { caseSensitive = value; }
}
private bool caseSensitive;

ExcludeInclude 매개 변수는 검색에서 명시적으로 제외되거나 포함된 항목을 식별합니다. 기본적으로 cmdlet은 데이터 저장소의 모든 항목을 검색합니다. 그러나 cmdlet에서 수행하는 검색을 제한하기 위해 이러한 매개 변수를 사용하여 검색에 포함하거나 생략할 항목을 명시적으로 나타낼 수 있습니다.

[Parameter]
public SwitchParameter CaseSensitive
{
  get { return caseSensitive; }
  set { caseSensitive = value; }
}
private bool caseSensitive;
[Parameter]
[ValidateNotNullOrEmpty]
public string[] Include
{
  get
  {
    return includeStrings;
  }
  set
  {
    includeStrings = value;

    this.include = new WildcardPattern[includeStrings.Length];
    for (int i = 0; i < includeStrings.Length; i++)
    {
      this.include[i] = new WildcardPattern(includeStrings[i], WildcardOptions.IgnoreCase);
    }
  }
}

internal string[] includeStrings = null;
internal WildcardPattern[] include = null;

매개 변수 집합 선언

이 cmdlet은 데이터 액세스에 사용되는 두 매개 변수 집합의 이름으로 두 개의 매개 변수 집합(기본값인ScriptParameterSetPatternParameterSet)을 사용합니다. PatternParameterSet 기본 매개 변수 집합이며 Pattern 매개 변수를 지정할 때 사용됩니다. ScriptParameterSet 사용자가 Script 매개 변수를 통해 대체 검색 메커니즘을 지정할 때 사용됩니다. 매개 변수 집합에 대한 자세한 내용은 cmdlet매개 변수 집합 추가를 참조하세요.

입력 처리 방법 재정의

cmdlet은 System.Management.Automation.PSCmdlet 클래스에 대한 하나 이상의 입력 처리 메서드를 재정의해야 합니다. 입력 처리 방법에 대한 자세한 내용은 첫 번째 Cmdlet 만들기참조하세요.

이 cmdlet은 System.Management.Automation.Cmdlet.BeginProcessing 메서드를 재정의하여 시작 시 컴파일된 정규식 배열을 빌드합니다. 이렇게 하면 단순 일치를 사용하지 않는 검색 중에 성능이 향상됩니다.

protected override void BeginProcessing()
{
  WriteDebug("Validating patterns.");
  if (patterns != null)
  {
    foreach(string pattern in patterns)
    {
      if (pattern == null)
      ThrowTerminatingError(new ErrorRecord(
                            new ArgumentNullException(
                            "Search pattern cannot be null."),
                            "NullSearchPattern",
                            ErrorCategory.InvalidArgument,
                            pattern)
                            );
    }

    WriteVerbose("Search pattern(s) are valid.");

    // If a simple match is not specified, then
    // compile the regular expressions once.
    if (!simpleMatch)
    {
      WriteDebug("Compiling search regular expressions.");

      RegexOptions regexOptions = RegexOptions.Compiled;
      if (!caseSensitive)
         regexOptions |= RegexOptions.Compiled;
      regexPattern = new Regex[patterns.Length];

      for (int i = 0; i < patterns.Length; i++)
      {
        try
        {
          regexPattern[i] = new Regex(patterns[i], regexOptions);
        }
        catch (ArgumentException ex)
        {
          ThrowTerminatingError(new ErrorRecord(
                        ex,
                        "InvalidRegularExpression",
                        ErrorCategory.InvalidArgument,
                        patterns[i]
                     ));
        }
      } //Loop through patterns to create RegEx objects.

      WriteVerbose("Pattern(s) compiled into regular expressions.");
    }// If not a simple match.

    // If a simple match is specified, then compile the
    // wildcard patterns once.
    else
    {
      WriteDebug("Compiling search wildcards.");

      WildcardOptions wildcardOptions = WildcardOptions.Compiled;

      if (!caseSensitive)
      {
        wildcardOptions |= WildcardOptions.IgnoreCase;
      }

      wildcardPattern = new WildcardPattern[patterns.Length];
      for (int i = 0; i < patterns.Length; i++)
      {
        wildcardPattern[i] =
                     new WildcardPattern(patterns[i], wildcardOptions);
      }

      WriteVerbose("Pattern(s) compiled into wildcard expressions.");
    }// If match is a simple match.
  }// If valid patterns are available.
}// End of function BeginProcessing().

또한 이 cmdlet은 System.Management.Automation.Cmdlet.ProcessRecord 메서드를 재정의하여 사용자가 명령줄에서 선택한 문자열을 처리합니다. private MatchString 메서드를 호출하여 문자열 선택 결과를 사용자 지정 개체 형식으로 작성합니다.

protected override void ProcessRecord()
{
  UInt64 lineNumber = 0;
  MatchInfo result;
  ArrayList nonMatches = new ArrayList();

  // Walk the list of paths and search the contents for
  // any of the specified patterns.
  foreach (string psPath in paths)
  {
    // Once the filepaths are expanded, we may have more than one
    // path, so process all referenced paths.
    foreach(PathInfo path in
            SessionState.Path.GetResolvedPSPathFromPSPath(psPath)
           )
    {
      WriteVerbose("Processing path " + path.Path);

      // Check if the path represents one of the items to be
      // excluded. If so, continue to next path.
      if (!MeetsIncludeExcludeCriteria(path.ProviderPath))
         continue;

      // Get the content reader for the item(s) at the
      // specified path.
      Collection<IContentReader> readerCollection = null;
      try
      {
        readerCollection =
                    this.InvokeProvider.Content.GetReader(path.Path);
      }
      catch (PSNotSupportedException ex)
      {
        WriteError(new ErrorRecord(ex,
                   "ContentAccessNotSupported",
                    ErrorCategory.NotImplemented,
                    path.Path)
                   );
        return;
      }

      foreach(IContentReader reader in readerCollection)
      {
        // Reset the line number for this path.
        lineNumber = 0;

        // Read in a single block (line in case of a file)
        // from the object.
        IList items = reader.Read(1);

        // Read and process one block(line) at a time until
        // no more blocks(lines) exist.
        while (items != null && items.Count == 1)
        {
          // Increment the line number each time a line is
          // processed.
          lineNumber++;

          String message = String.Format("Testing line {0} : {1}",
                                        lineNumber, items[0]);

          WriteDebug(message);

          result = SelectString(items[0]);

          if (result != null)
          {
            result.Path = path.Path;
            result.LineNumber = lineNumber;

            WriteObject(result);
          }
          else
          {
            // Add the block(line) that did not match to the
            // collection of non matches , which will be stored
            // in the SessionState variable $NonMatches
            nonMatches.Add(items[0]);
          }

          // Get the next line from the object.
          items = reader.Read(1);

        }// While loop for reading one line at a time.
      }// Foreach loop for reader collection.
    }// Foreach loop for processing referenced paths.
  }// Foreach loop for walking of path list.

  // Store the list of non-matches in the
  // session state variable $NonMatches.
  try
  {
    this.SessionState.PSVariable.Set("NonMatches", nonMatches);
  }
  catch (SessionStateUnauthorizedAccessException ex)
  {
    WriteError(new ErrorRecord(ex,
               "CannotWriteVariableNonMatches",
               ErrorCategory.InvalidOperation,
               nonMatches)
              );
  }

}// End of protected override void ProcessRecord().

콘텐츠 액세스

cmdlet이 데이터에 액세스할 수 있도록 Windows PowerShell 경로로 표시된 공급자를 열어야 합니다. runspace에 대한 System.Management.Automation.SessionState 개체는 공급자에 액세스하는 데 사용되고 cmdlet의 System.Management.Automation.PSCmdlet.InvokeProvider* 속성은 공급자를 여는 데 사용됩니다. 콘텐츠에 대한 액세스는 열린 공급자에 대한 System.Management.Automation.ProviderIntrinsics 개체를 검색하여 제공됩니다.

이 샘플 Select-Str cmdlet은 System.Management.Automation.ProviderIntrinsics.Content* 속성을 사용하여 검색할 콘텐츠를 노출합니다. 그런 다음 필요한 Windows PowerShell 경로를 전달하여 System.Management.Automation.ContentCmdletProviderIntrinsics.GetReader* 메서드를 호출할 수 있습니다.

코드 샘플

다음 코드는 이 Select-Str cmdlet의 이 버전 구현을 보여줍니다. 이 코드에는 cmdlet 클래스, cmdlet에서 사용하는 프라이빗 메서드 및 cmdlet을 등록하는 데 사용되는 Windows PowerShell 스냅인 코드가 포함됩니다. cmdlet을 등록하는 방법에 대한 자세한 내용은 빌드를 참조하세요.

//
// Copyright (c) 2006 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
using System;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Provider;
using System.ComponentModel;

namespace Microsoft.Samples.PowerShell.Commands
{
  #region SelectStringCommand
  /// <summary>
  /// This cmdlet searches through PSObjects for particular patterns.
  /// </summary>
  /// <remarks>
  /// This cmdlet can be used to search any object, such as a file or a
  /// variable, whose provider exposes methods for reading and writing
  /// content.
  /// </remarks>
  [Cmdlet(VerbsCommon.Select, "Str", DefaultParameterSetName="PatternParameterSet")]
  public class SelectStringCommand : PSCmdlet
  {
    #region Parameters
    /// <summary>
    /// Declare a Path parameter that specifies where the data is stored.
    /// This parameter must specify a PowerShell that indicates the
    /// PowerShell provider that is used to access the objects to be
    /// searched for matching patterns. This parameter should also have
    /// a PSPath alias to provide consistency with other cmdlets that use
    /// PowerShell providers.
    /// </summary>
    /// <value>Path of the object(s) to search.</value>
    [Parameter(
               Position = 0,
               ParameterSetName = "ScriptParameterSet",
               Mandatory = true)]
    [Parameter(
               Position = 0,
               ParameterSetName = "PatternParameterSet",
               ValueFromPipeline = true,
               Mandatory = true)]
               [Alias("PSPath")]
    public string[] Path
    {
      get { return paths; }
      set { paths = value; }
    }
    private string[] paths;

    /// <summary>
    /// Declare a Pattern parameter that specifies the pattern(s)
    /// used to find matching patterns in the string representation
    /// of the objects. A positive result will be returned
    /// if any of the patterns are found in the objects.
    /// </summary>
    /// <remarks>
    /// The patterns will be compiled into an array of wildcard
    /// patterns for a simple match (literal string matching),
    /// or the patterns will be converted into an array of compiled
    /// regular expressions.
    /// </remarks>
    /// <value>Array of patterns to search.</value>
    [Parameter(
               Position = 1,
               ParameterSetName = "PatternParameterSet",
               Mandatory = true)]
    public string[] Pattern
    {
      get { return patterns; }
      set { patterns = value; }
    }
    private string[] patterns;
    private Regex[] regexPattern;
    private WildcardPattern[] wildcardPattern;

    /// <summary>
    /// Declare a Script parameter that specifies a script block
    /// that is called to perform the matching operations
    /// instead of the matching performed by the cmdlet.
    /// </summary>
    /// <value>Script block that will be called for matching</value>
    [Parameter(
               Position = 1,
               ParameterSetName = "ScriptParameterSet",
               Mandatory = true)]
    public ScriptBlock Script
    {
      set { script = value; }
      get { return script; }
    }
    ScriptBlock script;

    /// <summary>
    /// Declare a switch parameter that specifies if the pattern(s) are used
    /// literally. If not (default), searching is
    /// done using regular expressions.
    /// </summary>
    /// <value>If True, a literal pattern is used.</value>
    [Parameter]
    public SwitchParameter SimpleMatch
    {
      get { return simpleMatch; }
      set { simpleMatch = value; }
    }
    private bool simpleMatch;

    /// <summary>
    /// Declare a switch parameter that specifies if a case-sensitive
    /// search is performed.  If not (default), a case-insensitive search
    /// is performed.
    /// </summary>
    /// <value>If True, a case-sensitive search is made.</value>
    [Parameter]
    public SwitchParameter CaseSensitive
    {
      get { return caseSensitive; }
      set { caseSensitive = value; }
    }
    private bool caseSensitive;

    /// <summary>
    /// Declare an Include parameter that species which
    /// specific items are searched.  When this parameter
    /// is used, items that are not listed here are omitted
    /// from the search.
    /// </summary>
    [Parameter]
    [ValidateNotNullOrEmpty]
    public string[] Include
    {
      get
      {
        return includeStrings;
      }
      set
      {
        includeStrings = value;

        this.include = new WildcardPattern[includeStrings.Length];
        for (int i = 0; i < includeStrings.Length; i++)
        {
          this.include[i] = new WildcardPattern(includeStrings[i], WildcardOptions.IgnoreCase);
        }
      }
    }

    internal string[] includeStrings = null;
    internal WildcardPattern[] include = null;

    /// <summary>
    /// Declare an Exclude parameter that species which
    /// specific items are omitted from the search.
    /// </summary>
    ///
    [Parameter]
    [ValidateNotNullOrEmpty]
    public string[] Exclude
    {
      get
      {
        return excludeStrings;
      }
      set
      {
        excludeStrings = value;

        this.exclude = new WildcardPattern[excludeStrings.Length];
        for (int i = 0; i < excludeStrings.Length; i++)
        {
          this.exclude[i] = new WildcardPattern(excludeStrings[i], WildcardOptions.IgnoreCase);
        }
      }
    }
    internal string[] excludeStrings;
    internal WildcardPattern[] exclude;

    #endregion Parameters

    #region Overrides
    /// <summary>
    /// If regular expressions are used for pattern matching,
    /// then build an array of compiled regular expressions
    /// at startup. This increases performance during scanning
    /// operations when simple matching is not used.
    /// </summary>
    protected override void BeginProcessing()
    {
      WriteDebug("Validating patterns.");
      if (patterns != null)
      {
        foreach(string pattern in patterns)
        {
          if (pattern == null)
          ThrowTerminatingError(new ErrorRecord(
                                new ArgumentNullException(
                                "Search pattern cannot be null."),
                                "NullSearchPattern",
                                ErrorCategory.InvalidArgument,
                                pattern)
                                );
        }

        WriteVerbose("Search pattern(s) are valid.");

        // If a simple match is not specified, then
        // compile the regular expressions once.
        if (!simpleMatch)
        {
          WriteDebug("Compiling search regular expressions.");

          RegexOptions regexOptions = RegexOptions.Compiled;
          if (!caseSensitive)
             regexOptions |= RegexOptions.Compiled;
          regexPattern = new Regex[patterns.Length];

          for (int i = 0; i < patterns.Length; i++)
          {
            try
            {
              regexPattern[i] = new Regex(patterns[i], regexOptions);
            }
            catch (ArgumentException ex)
            {
              ThrowTerminatingError(new ErrorRecord(
                            ex,
                            "InvalidRegularExpression",
                            ErrorCategory.InvalidArgument,
                            patterns[i]
                         ));
            }
          } //Loop through patterns to create RegEx objects.

          WriteVerbose("Pattern(s) compiled into regular expressions.");
        }// If not a simple match.

        // If a simple match is specified, then compile the
        // wildcard patterns once.
        else
        {
          WriteDebug("Compiling search wildcards.");

          WildcardOptions wildcardOptions = WildcardOptions.Compiled;

          if (!caseSensitive)
          {
            wildcardOptions |= WildcardOptions.IgnoreCase;
          }

          wildcardPattern = new WildcardPattern[patterns.Length];
          for (int i = 0; i < patterns.Length; i++)
          {
            wildcardPattern[i] =
                         new WildcardPattern(patterns[i], wildcardOptions);
          }

          WriteVerbose("Pattern(s) compiled into wildcard expressions.");
        }// If match is a simple match.
      }// If valid patterns are available.
    }// End of function BeginProcessing().

    /// <summary>
    /// Process the input and search for the specified patterns.
    /// </summary>
    protected override void ProcessRecord()
    {
      UInt64 lineNumber = 0;
      MatchInfo result;
      ArrayList nonMatches = new ArrayList();

      // Walk the list of paths and search the contents for
      // any of the specified patterns.
      foreach (string psPath in paths)
      {
        // Once the filepaths are expanded, we may have more than one
        // path, so process all referenced paths.
        foreach(PathInfo path in
                SessionState.Path.GetResolvedPSPathFromPSPath(psPath)
               )
        {
          WriteVerbose("Processing path " + path.Path);

          // Check if the path represents one of the items to be
          // excluded. If so, continue to next path.
          if (!MeetsIncludeExcludeCriteria(path.ProviderPath))
             continue;

          // Get the content reader for the item(s) at the
          // specified path.
          Collection<IContentReader> readerCollection = null;
          try
          {
            readerCollection =
                        this.InvokeProvider.Content.GetReader(path.Path);
          }
          catch (PSNotSupportedException ex)
          {
            WriteError(new ErrorRecord(ex,
                       "ContentAccessNotSupported",
                        ErrorCategory.NotImplemented,
                        path.Path)
                       );
            return;
          }

          foreach(IContentReader reader in readerCollection)
          {
            // Reset the line number for this path.
            lineNumber = 0;

            // Read in a single block (line in case of a file)
            // from the object.
            IList items = reader.Read(1);

            // Read and process one block(line) at a time until
            // no more blocks(lines) exist.
            while (items != null && items.Count == 1)
            {
              // Increment the line number each time a line is
              // processed.
              lineNumber++;

              String message = String.Format("Testing line {0} : {1}",
                                            lineNumber, items[0]);

              WriteDebug(message);

              result = SelectString(items[0]);

              if (result != null)
              {
                result.Path = path.Path;
                result.LineNumber = lineNumber;

                WriteObject(result);
              }
              else
              {
                // Add the block(line) that did not match to the
                // collection of non matches , which will be stored
                // in the SessionState variable $NonMatches
                nonMatches.Add(items[0]);
              }

              // Get the next line from the object.
              items = reader.Read(1);

            }// While loop for reading one line at a time.
          }// Foreach loop for reader collection.
        }// Foreach loop for processing referenced paths.
      }// Foreach loop for walking of path list.

      // Store the list of non-matches in the
      // session state variable $NonMatches.
      try
      {
        this.SessionState.PSVariable.Set("NonMatches", nonMatches);
      }
      catch (SessionStateUnauthorizedAccessException ex)
      {
        WriteError(new ErrorRecord(ex,
                   "CannotWriteVariableNonMatches",
                   ErrorCategory.InvalidOperation,
                   nonMatches)
                  );
      }

    }// End of protected override void ProcessRecord().
    #endregion Overrides

    #region PrivateMethods
    /// <summary>
    /// Check for a match using the input string and the pattern(s)
    /// specified.
    /// </summary>
    /// <param name="input">The string to test.</param>
    /// <returns>MatchInfo object containing information about
    /// result of a match</returns>
    private MatchInfo SelectString(object input)
    {
      string line = null;

      try
      {
        // Convert the object to a string type
        // safely using language support methods
        line = (string)LanguagePrimitives.ConvertTo(
                                                    input,
                                                    typeof(string)
                                                    );
        line = line.Trim(' ','\t');
      }
      catch (PSInvalidCastException ex)
      {
        WriteError(new ErrorRecord(
                   ex,
                   "CannotCastObjectToString",
                   ErrorCategory.InvalidOperation,
                   input)
                   );

        return null;
      }

      MatchInfo result = null;

      // If a scriptblock has been specified, call it
      // with the path for processing.  It will return
      // one object.
      if (script != null)
      {
        WriteDebug("Executing script block.");

        Collection<PSObject> psObjects =
                             script.Invoke(
                                           line,
                                           simpleMatch,
                                           caseSensitive
                                          );

        foreach (PSObject psObject in psObjects)
        {
          if (LanguagePrimitives.IsTrue(psObject))
          {
            result = new MatchInfo();
            result.Line = line;
            result.IgnoreCase = !caseSensitive;

            break;
          } //End of If.
        } //End ForEach loop.
      } // End of If if script exists.

      // If script block exists, see if this line matches any
      // of the match patterns.
      else
      {
        int patternIndex = 0;

        while (patternIndex < patterns.Length)
        {
          if ((simpleMatch &&
              wildcardPattern[patternIndex].IsMatch(line))
              || (regexPattern != null
              && regexPattern[patternIndex].IsMatch(line))
             )
          {
            result = new MatchInfo();
            result.IgnoreCase = !caseSensitive;
            result.Line = line;
            result.Pattern = patterns[patternIndex];

            break;
          }

          patternIndex++;

        }// While loop through patterns.
      }// Else for no script block specified.

      return result;

    }// End of SelectString

    /// <summary>
    /// Check whether the supplied name meets the include/exclude criteria.
    /// That is - it's on the include list if the include list was
    /// specified, and not on the exclude list if the exclude list was specified.
    /// </summary>
    /// <param name="path">path to validate</param>
    /// <returns>True if the path is acceptable.</returns>
    private bool MeetsIncludeExcludeCriteria(string path)
    {
      bool ok = false;

      // See if the file is on the include list.
      if (this.include != null)
      {
        foreach (WildcardPattern patternItem in this.include)
        {
          if (patternItem.IsMatch(path))
          {
            ok = true;
            break;
          }
        }
      }
      else
      {
        ok = true;
      }

      if (!ok)
         return false;

      // See if the file is on the exclude list.
      if (this.exclude != null)
      {
        foreach (WildcardPattern patternItem in this.exclude)
        {
          if (patternItem.IsMatch(path))
          {
            ok = false;
            break;
          }
        }
      }

      return ok;
    } //MeetsIncludeExcludeCriteria
    #endregion Private Methods

  }// class SelectStringCommand

  #endregion SelectStringCommand

  #region MatchInfo

  /// <summary>
  /// Class representing the result of a pattern/literal match
  /// that is passed through the pipeline by the Select-Str cmdlet.
  /// </summary>
  public class MatchInfo
  {
    /// <summary>
    /// Indicates if the match was done ignoring case.
    /// </summary>
    /// <value>True if case was ignored.</value>
    public bool IgnoreCase
    {
      get { return ignoreCase; }
      set { ignoreCase = value; }
    }
    private bool ignoreCase;

    /// <summary>
    /// Specifies the number of the matching line.
    /// </summary>
    /// <value>The number of the matching line.</value>
    public UInt64 LineNumber
    {
      get { return lineNumber; }
      set { lineNumber = value; }
    }
    private UInt64 lineNumber;

    /// <summary>
    /// Specifies the text of the matching line.
    /// </summary>
    /// <value>The text of the matching line.</value>
    public string Line
    {
      get { return line; }
      set { line = value; }
    }
    private string line;

    /// <summary>
    /// Specifies the full path of the object(file) containing the
    /// matching line.
    /// </summary>
    /// <remarks>
    /// It will be "inputStream" if the object came from the input
    /// stream.
    /// </remarks>
    /// <value>The path name</value>
    public string Path
    {
      get { return path; }
      set
      {
        pathSet = true;
        path = value;
      }
    }
    private string path;
    private bool pathSet;

    /// <summary>
    /// Specifies the pattern that was used in the match.
    /// </summary>
    /// <value>The pattern string</value>
    public string Pattern
    {
      get { return pattern; }
      set { pattern = value; }
    }
    private string pattern;

    private const string MatchFormat = "{0}:{1}:{2}";

    /// <summary>
    /// Returns the string representation of this object. The format
    /// depends on whether a path has been set for this object or
    /// not.
    /// </summary>
    /// <remarks>
    /// If the path component is set, as would be the case when
    /// matching in a file, ToString() returns the path, line
    /// number and line text.  If path is not set, then just the
    /// line text is presented.
    /// </remarks>
    /// <returns>The string representation of the match object.</returns>
    public override string ToString()
    {
      if (pathSet)
         return String.Format(
         System.Threading.Thread.CurrentThread.CurrentCulture,
         MatchFormat,
         this.path,
         this.lineNumber,
         this.line
         );
      else
         return this.line;
    }
  }// End class MatchInfo

  #endregion

  #region PowerShell snap-in

  /// <summary>
  /// Create a PowerShell snap-in for the Select-Str cmdlet.
  /// </summary>
  [RunInstaller(true)]
  public class SelectStringPSSnapIn : PSSnapIn
  {
    /// <summary>
    /// Create an instance of the SelectStrPSSnapin class.
    /// </summary>
    public SelectStringPSSnapIn()
           : base()
    {
    }

    /// <summary>
    /// Specify the name of the PowerShell snap-in.
    /// </summary>
    public override string Name
    {
      get
      {
        return "SelectStrPSSnapIn";
      }
    }

    /// <summary>
    /// Specify the vendor of the PowerShell snap-in.
    /// </summary>
    public override string Vendor
    {
      get
      {
        return "Microsoft";
      }
    }

    /// <summary>
    /// Specify the localization resource information for the vendor.
    /// Use the format: SnapinName,VendorName.
    /// </summary>
    public override string VendorResource
    {
      get
      {
        return "SelectStrSnapIn,Microsoft";
      }
    }

    /// <summary>
    /// Specify the description of the PowerShell snap-in.
    /// </summary>
    public override string Description
    {
      get
        {
          return "This is a PowerShell snap-in for the Select-Str cmdlet.";
        }
    }

    /// <summary>
    /// Specify the localization resource information for the description.
    /// Use the format: SnapinName,Description.

    /// </summary>
    public override string DescriptionResource
    {
      get
      {
          return "SelectStrSnapIn,This is a PowerShell snap-in for the Select-Str cmdlet.";
      }
    }
  }
  #endregion PowerShell snap-in

} //namespace Microsoft.Samples.PowerShell.Commands;

Cmdlet 빌드

cmdlet을 구현한 후 Windows PowerShell 스냅인을 통해 Windows PowerShell에 등록해야 합니다. cmdlet 등록에 대한 자세한 내용은 Cmdlet, 공급자 및 호스트 애플리케이션등록하는 방법을 참조하세요.

Cmdlet 테스트

cmdlet이 Windows PowerShell에 등록되면 명령줄에서 실행하여 테스트할 수 있습니다. 다음 절차를 사용하여 샘플 Select-Str cmdlet을 테스트할 수 있습니다.

  1. Windows PowerShell을 시작하고 Notes 파일에서 ".NET" 식을 사용하여 줄이 나타나는 항목을 검색합니다. 경로가 둘 이상의 단어로 구성된 경우에만 경로 이름 주위에 따옴표가 필요합니다.

    Select-Str -Path "notes" -Pattern ".NET" -SimpleMatch=$false
    

    다음 출력이 나타납니다.

    IgnoreCase   : True
    LineNumber   : 8
    Line         : Because Windows PowerShell works directly with .NET objects, there is often a .NET object
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : .NET
    IgnoreCase   : True
    LineNumber   : 21
    Line         : You should normally define the class for a cmdlet in a .NET namespace
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : .NET
    
  2. 메모 파일에서 "over"라는 단어와 다른 텍스트가 있는 줄이 있는지 검색합니다. SimpleMatch 매개 변수는 false기본값을 사용합니다. CaseSensitive 매개 변수가 false설정되므로 검색은 대/소문자를 구분하지 않습니다.

    Select-Str -Path notes -Pattern "over*" -SimpleMatch -CaseSensitive:$false
    

    다음 출력이 나타납니다.

    IgnoreCase   : True
    LineNumber   : 45
    Line         : Override StopProcessing
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : over*
    IgnoreCase   : True
    LineNumber   : 49
    Line         : overriding the StopProcessing method
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : over*
    
  3. 정규식을 사용하여 Notes 파일을 패턴으로 검색합니다. cmdlet은 사전순 문자와 괄호로 묶인 빈 공백을 검색합니다.

    Select-Str -Path notes -Pattern "\([A-Za-z:blank:]" -SimpleMatch:$false
    

    다음 출력이 나타납니다.

    IgnoreCase   : True
    LineNumber   : 1
    Line         : Advisory Guidelines (Consider Following)
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : \([A-Za-z:blank:]
    IgnoreCase   : True
    LineNumber   : 53
    Line         : If your cmdlet has objects that are not disposed of (written to the pipeline)
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : \([A-Za-z:blank:]
    
  4. 메모 파일의 대/소문자 구분 검색을 수행하여 "매개 변수"라는 단어를 찾습니다.

    Select-Str -Path notes -Pattern Parameter -CaseSensitive
    

    다음 출력이 나타납니다.

    IgnoreCase   : False
    LineNumber   : 6
    Line         : Support an InputObject Parameter
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : Parameter
    IgnoreCase   : False
    LineNumber   : 30
    Line         : Support Force Parameter
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\notes
    Pattern      : Parameter
    
  5. Windows PowerShell과 함께 제공되는 변수 공급자에서 숫자 값이 0에서 9까지인 변수를 검색합니다.

    Select-Str -Path * -Pattern "[0-9]"
    

    다음 출력이 나타납니다.

    IgnoreCase   : True
    LineNumber   : 1
    Line         : 64
    Path         : Variable:\MaximumHistoryCount
    Pattern      : [0-9]
    
  6. 스크립트 블록을 사용하여 파일 SelectStrCommandSample.cs "Pos" 문자열을 검색합니다. -cmatch 연산자는 대/소문자를 구분하지 않는 패턴 일치를 수행합니다.

    Select-Str -Path "SelectStrCommandSample.cs" -Script { if ($args[0] -cmatch "Pos"){ return $true } return $false }
    

    다음 출력이 나타납니다.

    IgnoreCase   : True
    LineNumber   : 37
    Line         :    Position = 0.
    Path         : C:\PowerShell-Progs\workspace\Samples\SelectStr\SelectStrCommandSample.cs
    Pattern      :
    

또한 참조하십시오

Windows PowerShell Cmdlet 만드는 방법

첫 번째 cmdlet 만들기

시스템 수정하는 Cmdlet 만들기

Windows PowerShell 공급자 디자인

Windows PowerShell 작동 방식

Cmdlet, 공급자 및 호스트 애플리케이션 등록하는 방법

Windows PowerShell SDK