diff --git a/src/System.Management.Automation/engine/lang/parserutils.cs b/src/System.Management.Automation/engine/lang/parserutils.cs
index 3be7adb86b..354c63b883 100644
--- a/src/System.Management.Automation/engine/lang/parserutils.cs
+++ b/src/System.Management.Automation/engine/lang/parserutils.cs
@@ -881,8 +881,8 @@ namespace System.Management.Automation
/// The result of the operator
internal static object ReplaceOperator(ExecutionContext context, IScriptExtent errorPosition, object lval, object rval, bool ignoreCase)
{
- string replacement = "";
object pattern = "";
+ object substitute = "";
rval = PSObject.Base(rval);
IList rList = rval as IList;
@@ -895,13 +895,14 @@ namespace System.Management.Automation
"BadReplaceArgument", ParserStrings.BadReplaceArgument, ignoreCase ? "-ireplace" : "-replace", rList.Count);
}
+ if(rList.Count > 1)
+ {
+ substitute = rList[1];
+ }
+
if (rList.Count > 0)
{
pattern = rList[0];
- if (rList.Count > 1)
- {
- replacement = PSObject.ToStringParser(context, rList[1]);
- }
}
}
else
@@ -935,8 +936,7 @@ namespace System.Management.Automation
{
string lvalString = lval?.ToString() ?? String.Empty;
- // Find a single match in the string.
- return rr.Replace(lvalString, replacement);
+ return ReplaceOperatorImpl(context, lvalString, rr, substitute);
}
else
{
@@ -944,14 +944,35 @@ namespace System.Management.Automation
while (ParserOps.MoveNext(context, errorPosition, list))
{
string lvalString = PSObject.ToStringParser(context, ParserOps.Current(errorPosition, list));
-
- resultList.Add(rr.Replace(lvalString, replacement));
+ resultList.Add(ReplaceOperatorImpl(context, lvalString, rr, substitute));
}
return resultList.ToArray();
}
}
+ ///
+ /// ReplaceOperator implementation.
+ /// Abstracts away conversion of the optional substitute parameter to either a string or a MatchEvaluator delegate
+ /// and finally returns the result of the final Regex.Replace operation.
+ ///
+ /// The execution context in which to evaluate the expression
+ /// The input string
+ /// A Regex instance.
+ /// The substitute value
+ /// The result of the regex.Replace operation
+ private static object ReplaceOperatorImpl(ExecutionContext context, string input, Regex regex, object substitute)
+ {
+ MatchEvaluator matchEvaluator = null;
+ if (!(substitute is string) && LanguagePrimitives.TryConvertTo(substitute, out matchEvaluator))
+ {
+ return regex.Replace(input, matchEvaluator);
+ }
+
+ string replacement = PSObject.ToStringParser(context, substitute);
+ return regex.Replace(input, replacement);
+ }
+
///
/// Implementation of the PowerShell type operators...
///
@@ -1906,4 +1927,3 @@ namespace System.Management.Automation
}
#endregion ScriptTrace
}
-
diff --git a/test/powershell/Language/Operators/ReplaceOperator.Tests.ps1 b/test/powershell/Language/Operators/ReplaceOperator.Tests.ps1
new file mode 100644
index 0000000000..2cb807d86d
--- /dev/null
+++ b/test/powershell/Language/Operators/ReplaceOperator.Tests.ps1
@@ -0,0 +1,64 @@
+Describe "Replace Operator" -Tags CI {
+ Context "Replace operator" {
+ It "Replace operator can replace string values using regular expressions" {
+ $res = "Get-Process" -replace "Get", "Stop"
+ $res | Should BeExactly "Stop-Process"
+
+ $res = "image.gif" -replace "\.gif$",".jpg"
+ $res | Should BeExactly "image.jpg"
+ }
+
+ It "Replace operator can be case-insensitive and case-sensitive" {
+ $res = "book" -replace "B","C"
+ $res | Should BeExactly "Cook"
+
+ $res = "book" -ireplace "B","C"
+ $res | Should BeExactly "Cook"
+
+ $res = "book" -creplace "B","C"
+ $res | Should BeExactly "book"
+ }
+
+ It "Replace operator can take 2 arguments, a mandatory pattern, and an optional substitution" {
+ $res = "PowerPoint" -replace "Point","Shell"
+ $res | Should BeExactly "PowerShell"
+
+ $res = "PowerPoint" -replace "Point"
+ $res | Should BeExactly "Power"
+ }
+ }
+
+ Context "Replace operator substitutions" {
+ It "Replace operator supports numbered substitution groups using ```$n" {
+ $res = "domain.example" -replace ".*\.(\w+)$","Tld of '`$0' is - '`$1'"
+ $res | Should BeExactly "Tld of 'domain.example' is - 'example'"
+ }
+
+ It "Replace operator supports named substitution groups using ```${name}" {
+ $res = "domain.example" -replace ".*\.(?\w+)$","`${tld}"
+ $res | Should BeExactly "example"
+ }
+
+ It "Replace operator can take a ScriptBlock in place of a substitution string" {
+ $res = "ID ABC123" -replace "\b[A-C]+", {return "X" * $args[0].Value.Length}
+ $res | Should BeExactly "ID XXX123"
+ }
+
+ It "Replace operator can take a MatchEvaluator in place of a substitution string" {
+ $matchEvaluator = {return "X" * $args[0].Value.Length} -as [System.Text.RegularExpressions.MatchEvaluator]
+ $res = "ID ABC123" -replace "\b[A-C]+", $matchEvaluator
+ $res | Should BeExactly "ID XXX123"
+ }
+
+ It "Replace operator can take a static PSMethod in place of a substitution string" {
+ class R {
+ static [string] Replace([System.Text.RegularExpressions.Match]$Match) {
+ return "X" * $Match.Value.Length
+ }
+ }
+ $substitutionMethod = [R]::Replace
+ $res = "ID 0000123" -replace "\b0+", $substitutionMethod
+ $res | Should BeExactly "ID XXXX123"
+ }
+ }
+}