diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs
index 09536ea00f..110dea324d 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs
@@ -289,10 +289,18 @@ namespace Microsoft.PowerShell.Commands
///
/// A cmdlet to search through strings and files for particular patterns.
///
- [Cmdlet(VerbsCommon.Select, "String", DefaultParameterSetName = "File", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113388")]
- [OutputType(typeof(MatchInfo), typeof(bool))]
+ [Cmdlet(VerbsCommon.Select, "String", DefaultParameterSetName = ParameterSetFile, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113388")]
+ [OutputType(typeof(bool), typeof(MatchInfo), ParameterSetName = new[] { ParameterSetFile, ParameterSetObject, ParameterSetLiteralFile })]
+ [OutputType(typeof(string), ParameterSetName = new[] { ParameterSetFileRaw, ParameterSetObjectRaw, ParameterSetLiteralFileRaw })]
public sealed class SelectStringCommand : PSCmdlet
{
+ private const string ParameterSetFile = "File";
+ private const string ParameterSetFileRaw = "FileRaw";
+ private const string ParameterSetObject = "Object";
+ private const string ParameterSetObjectRaw = "ObjectRaw";
+ private const string ParameterSetLiteralFile = "LiteralFile";
+ private const string ParameterSetLiteralFileRaw = "LiteralFileRaw";
+
///
/// A generic circular buffer.
///
@@ -965,7 +973,8 @@ namespace Microsoft.PowerShell.Commands
///
/// Gets or sets the current pipeline object.
///
- [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = "Object")]
+ [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = ParameterSetObject)]
+ [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = ParameterSetObjectRaw)]
[AllowNull]
[AllowEmptyString]
public PSObject InputObject
@@ -988,7 +997,8 @@ namespace Microsoft.PowerShell.Commands
/// Gets or sets files to read from.
/// Globbing is done on these.
///
- [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "File")]
+ [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetFile)]
+ [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetFileRaw)]
[FileinfoToString]
public string[] Path { get; set; }
@@ -996,7 +1006,8 @@ namespace Microsoft.PowerShell.Commands
/// Gets or sets literal files to read from.
/// Globbing is not done on these.
///
- [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "LiteralFile")]
+ [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetLiteralFile)]
+ [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetLiteralFileRaw)]
[FileinfoToString]
[Alias("PSPath", "LP")]
public string[] LiteralPath
@@ -1011,6 +1022,15 @@ namespace Microsoft.PowerShell.Commands
private bool _isLiteralPath;
+ ///
+ /// Gets or sets a value indicating if only string values containing matched lines should be returned.
+ /// If not (default) return MatchInfo (or bool objects, when Quiet is passed).
+ ///
+ [Parameter(Mandatory = true, ParameterSetName = ParameterSetObjectRaw)]
+ [Parameter(Mandatory = true, ParameterSetName = ParameterSetFileRaw)]
+ [Parameter(Mandatory = true, ParameterSetName = ParameterSetLiteralFileRaw)]
+ public SwitchParameter Raw { get; set; }
+
///
/// Gets or sets a value indicating if a pattern string should be matched literally.
/// If not (default) search using pattern as a Regular Expression.
@@ -1028,7 +1048,9 @@ namespace Microsoft.PowerShell.Commands
/// Gets or sets a value indicating if the cmdlet will stop processing at the first successful match and
/// return true. If both List and Quiet parameters are given, an exception is thrown.
///
- [Parameter]
+ [Parameter(ParameterSetName = ParameterSetObject)]
+ [Parameter(ParameterSetName = ParameterSetFile)]
+ [Parameter(ParameterSetName = ParameterSetLiteralFile)]
public SwitchParameter Quiet { get; set; }
///
@@ -1160,7 +1182,10 @@ namespace Microsoft.PowerShell.Commands
private int _postContext = 0;
- private IContextTracker GetContextTracker() => (_preContext == 0 && _postContext == 0) ? _noContextTracker : new ContextTracker(_preContext, _postContext);
+ // When we are in Raw mode or pre- and postcontext are zero, use the _noContextTracker, since we will not be needing trackedLines.
+ private IContextTracker GetContextTracker() => (Raw || (_preContext == 0 && _postContext == 0))
+ ? _noContextTracker
+ : new ContextTracker(_preContext, _postContext);
// This context tracker is only used for strings which are piped
// directly into the cmdlet. File processing doesn't need
@@ -1425,8 +1450,14 @@ namespace Microsoft.PowerShell.Commands
return false;
}
- // If -quiet is specified but not -list return true on first match
- if (Quiet && !List)
+ if (Raw)
+ {
+ foreach (MatchInfo match in contextTracker.EmitQueue)
+ {
+ WriteObject(match.Line);
+ }
+ }
+ else if (Quiet && !List)
{
WriteObject(true);
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1
index d7b9052453..861d5b1146 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1
@@ -68,7 +68,7 @@ Describe "Select-String" -Tags "CI" {
}
it "Should return an array of non matching strings when the switch of NotMatch is used and the string do not match" {
- $testinputone | Select-String -Pattern "goodbye" -NotMatch | Should -Be "hello", "hello"
+ $testinputone | Select-String -Pattern "goodbye" -NotMatch | Should -BeExactly "hello", "Hello"
}
it "Should return the same as NotMatch" {
@@ -76,7 +76,16 @@ Describe "Select-String" -Tags "CI" {
$secondMatch = $testinputone | Select-String -pattern "goodbye" -n
$equal = @(Compare-Object $firstMatch $secondMatch).Length -eq 0
- $equal | Should -Be True
+ $equal | Should -BeTrue
+ }
+
+ It "Should return a string type when -Raw is used" {
+ $result = $testinputtwo | Select-String -Pattern "hello" -CaseSensitive -Raw
+ $result | Should -BeOfType "System.String"
+ }
+
+ It "Should return ParameterBindingException when -Raw and -Quiet are used together" {
+ { $testinputone | Select-String -Pattern "hello" -Raw -Quiet -ErrorAction Stop } | Should -Throw -ExceptionType ([System.Management.Automation.ParameterBindingException])
}
}
@@ -106,13 +115,13 @@ Describe "Select-String" -Tags "CI" {
It "Should return the name of the file and the string that 'string' is found if there is only one lines that has a match" {
$expected = $testInputFile + ":1:This is a text string, and another string"
- Select-String $testInputFile -Pattern "string" | Should -Be $expected
+ Select-String $testInputFile -Pattern "string" | Should -BeExactly $expected
}
It "Should return all strings where 'second' is found in testfile1 if there is only one lines that has a match" {
$expected = $testInputFile + ":2:This is the second line"
- Select-String $testInputFile -Pattern "second"| Should -Be $expected
+ Select-String $testInputFile -Pattern "second"| Should -BeExactly $expected
}
It "Should return all strings where 'in' is found in testfile1 pattern switch is not required" {
@@ -121,10 +130,10 @@ Describe "Select-String" -Tags "CI" {
$expected3 = "This is the third line"
$expected4 = "This is the fourth line"
- (Select-String in $testInputFile)[0].Line | Should -Be $expected1
- (Select-String in $testInputFile)[1].Line | Should -Be $expected2
- (Select-String in $testInputFile)[2].Line | Should -Be $expected3
- (Select-String in $testInputFile)[3].Line | Should -Be $expected4
+ (Select-String in $testInputFile)[0].Line | Should -BeExactly $expected1
+ (Select-String in $testInputFile)[1].Line | Should -BeExactly $expected2
+ (Select-String in $testInputFile)[2].Line | Should -BeExactly $expected3
+ (Select-String in $testInputFile)[3].Line | Should -BeExactly $expected4
(Select-String in $testInputFile)[4].Line | Should -BeNullOrEmpty
}
@@ -175,6 +184,24 @@ Describe "Select-String" -Tags "CI" {
Select-String 'matc*' $testInputFile -ca | Should -Match $expected
}
+
+ It "Should return all strings where 'in' is found in testfile1, when -Raw is used." {
+ $expected1 = "This is a text string, and another string"
+ $expected2 = "This is the second line"
+ $expected3 = "This is the third line"
+ $expected4 = "This is the fourth line"
+
+ (Select-String in $testInputFile -Raw)[0] | Should -BeExactly $expected1
+ (Select-String in $testInputFile -Raw)[1] | Should -BeExactly $expected2
+ (Select-String in $testInputFile -Raw)[2] | Should -BeExactly $expected3
+ (Select-String in $testInputFile -Raw)[3] | Should -BeExactly $expected4
+ (Select-String in $testInputFile -Raw)[4] | Should -BeNullOrEmpty
+ }
+
+ It "Should ignore -Context parameter when -Raw is used." {
+ $expected = "This is the second line"
+ Select-String second $testInputFile -Raw -Context 2,2 | Should -BeExactly $expected
+ }
}
Push-Location $currentDirectory
}