本節說明如何建立 Cmdlet,以透過 Windows PowerShell 提供者存取儲存的數據。 這種類型的 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 會偵測特定字串,因此此處選擇的動詞名稱為 “Select”,由 System.Management.Automation.VerbsCommon 類別定義。 使用名詞名稱 「Str」,因為 Cmdlet 會作用於字串。 在下列宣告中,請注意 Cmdlet 動詞和名詞名稱會反映在 Cmdlet 類別的名稱中。 如需已核准 Cmdlet 動詞的詳細資訊,請參閱 Cmdlet 指令動詞名稱。
此 Cmdlet 的 .NET 類別必須衍生自 System.Management.Automation.PSCmdlet 基類,因為它提供 Windows PowerShell 運行時間公開 Windows PowerShell 提供者基礎結構所需的支援。 請注意,此 Cmdlet 也會使用 .NET Framework 正則表示式類別,例如 System.Text.RegularExpressions.Regex。
下列程式代碼是這個 Select-Str Cmdlet 的類別定義。
[Cmdlet(VerbsCommon.Select, "Str", DefaultParameterSetName="PatternParameterSet")]
public class SelectStringCommand : PSCmdlet
此 Cmdlet 會藉由將 DefaultParameterSetName attribute 關鍵詞新增至類別宣告,來定義預設參數集。 未指定 PatternParameterSet 參數時,會使用預設參數集 Script。 如需此參數集的詳細資訊,請參閱下一節中的 Pattern 和 Script 參數討論。
定義資料存取的參數
此 Cmdlet 會定義數個參數,讓用戶能夠存取和檢查儲存的數據。 這些參數包括 Path 參數,指出數據存放區的位置、指定要在搜尋中使用的模式的 Pattern 參數,以及數個支援搜尋執行方式的其他參數。
備註
欲了解更多參數定義基本資訊,請參閱[新增處理命令列輸入的參數][04]。
宣告 Path 參數
若要找出數據存放區,此 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 參數屬於 ScriptParameterSet 和 PatternParameterSet。 如需參數集的詳細資訊,請參閱 將參數集新增至 Cmdlet。
System.Management.Automation.AliasAttribute 屬性會宣告 PSPath 參數的 Path 別名。 強烈建議您宣告此別名,以便與其他存取 Windows PowerShell 提供者的 Cmdlet 保持一致。 如需 Windows PowerShell 路徑的詳細資訊,請參閱 windows PowerShell 的運作方式
宣告 Pattern 參數
若要指定要搜尋的模式,此 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 參數也可以用來提供包含模式的腳本。
Script 和 Pattern 參數會定義兩個不同的參數集,因此它們互斥。
宣告搜尋支持參數
此 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 參數是參數,指出是否執行區分大小寫的搜尋。 當使用者在命令行 (true) 指定 參數時,Cmdlet 會在比較模式時檢查字元的大寫和小寫。 如果未指定 參數 (false),Cmdlet 不會區分大寫和小寫。 例如,“MyFile” 和 “myfile” 都會傳回為正命中。 此參數預設值為 false。
[Parameter]
public SwitchParameter CaseSensitive
{
get { return caseSensitive; }
set { caseSensitive = value; }
}
private bool caseSensitive;
Exclude 和 Include 參數會識別在搜尋中明確排除或包含的專案。 根據預設,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 會使用兩個參數集(ScriptParameterSet 和 PatternParameterSet,這是預設值),做為數據存取中使用的兩個參數集的名稱。
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* 属性來公開要掃描的內容。 然後,它可以呼叫 System.Management.Automation.ContentCmdletProviderIntrinsics.GetReader* 方法,並傳遞必要的 Windows PowerShell 路徑。
程式碼範例
下列程式代碼顯示此 Select-Str Cmdlet 這個版本的實作。 請注意,此程式代碼包含 Cmdlet 類別、Cmdlet 所使用的私用方法,以及用來註冊 Cmdlet 的 Windows PowerShell 嵌入式管理單元程序代碼。 如需註冊 Cmdlet 的詳細資訊,請參閱建置 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。
啟動 Windows PowerShell,並使用表示式 “.NET” 搜尋 Notes 檔案中出現的行數。 請注意,只有在路徑包含一個以上的單字時,才需要路徑名稱周圍的引號。
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在 Notes 檔案中搜尋含有 「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*使用正則表達式做為模式來搜尋 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:]執行 Notes 檔案的區分大小寫搜尋,以尋找 「Parameter」 這個字的出現次數。
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搜尋 Windows PowerShell 隨附的變數提供者,以尋找數值從 0 到 9 的變數。
Select-Str -Path * -Pattern "[0-9]"下列輸出隨即出現。
IgnoreCase : True LineNumber : 1 Line : 64 Path : Variable:\MaximumHistoryCount Pattern : [0-9]使用腳本區塊來搜尋字串 「Pos」 的檔案SelectStrCommandSample.cs。
-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